<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=2">
<meta name="theme-color" content="#222">
<meta name="generator" content="Hexo 4.2.1">
  <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png">
  <link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32-next.png">
  <link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16-next.png">
  <link rel="mask-icon" href="/images/logo.svg" color="#222">

<link rel="stylesheet" href="/css/main.css">


<link rel="stylesheet" href="/lib/font-awesome/css/all.min.css">

<script id="hexo-configurations">
    var NexT = window.NexT || {};
    var CONFIG = {"hostname":"yoursite.com","root":"/","scheme":"Gemini","version":"7.8.0","exturl":false,"sidebar":{"position":"left","display":"always","padding":18,"offset":12,"onmobile":false},"copycode":{"enable":false,"show_result":false,"style":null},"back2top":{"enable":true,"sidebar":false,"scrollpercent":false},"bookmark":{"enable":false,"color":"#222","save":"auto"},"fancybox":false,"mediumzoom":false,"lazyload":false,"pangu":false,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},"algolia":{"hits":{"per_page":10},"labels":{"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}},"localsearch":{"enable":false,"trigger":"auto","top_n_per_article":1,"unescape":false,"preload":false},"motion":{"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}}};
  </script>

  <meta name="description" content="ES6 简介ECMAScript 6.0（以下简称 ES6）是 JavaScript 语言的下一代标准，&#x3D;&#x3D;已经在2015年6月正式发布。&#x3D;&#x3D; 它的目标，是使得 JavaScript 语言可以用来编写复杂的大型应用程序，成为企业级开发语言。 let 和 const 命令let 命令基本用法ES6 新增了 let 命令，用来声明变量。它的用法类似于 var，但是所声明的变量，&#x3D;&#x3D;只在 let 命令所">
<meta property="og:type" content="website">
<meta property="og:title" content="ECMAScript6">
<meta property="og:url" content="http://yoursite.com/ECMAScript6/index.html">
<meta property="og:site_name" content="Albert Guo">
<meta property="og:description" content="ES6 简介ECMAScript 6.0（以下简称 ES6）是 JavaScript 语言的下一代标准，&#x3D;&#x3D;已经在2015年6月正式发布。&#x3D;&#x3D; 它的目标，是使得 JavaScript 语言可以用来编写复杂的大型应用程序，成为企业级开发语言。 let 和 const 命令let 命令基本用法ES6 新增了 let 命令，用来声明变量。它的用法类似于 var，但是所声明的变量，&#x3D;&#x3D;只在 let 命令所">
<meta property="og:locale" content="zh_CN">
<meta property="article:published_time" content="2020-06-29T09:52:00.000Z">
<meta property="article:modified_time" content="2020-06-29T11:24:01.612Z">
<meta property="article:author" content="Albert Guo">
<meta name="twitter:card" content="summary">

<link rel="canonical" href="http://yoursite.com/ECMAScript6/">


<script id="page-configurations">
  // https://hexo.io/docs/variables.html
  CONFIG.page = {
    sidebar: "",
    isHome : false,
    isPost : false,
    lang   : 'zh-CN'
  };
</script>

  <title>ECMAScript6 | Albert Guo
</title>
  






  <noscript>
  <style>
  .use-motion .brand,
  .use-motion .menu-item,
  .sidebar-inner,
  .use-motion .post-block,
  .use-motion .pagination,
  .use-motion .comments,
  .use-motion .post-header,
  .use-motion .post-body,
  .use-motion .collection-header { opacity: initial; }

  .use-motion .site-title,
  .use-motion .site-subtitle {
    opacity: initial;
    top: initial;
  }

  .use-motion .logo-line-before i { left: initial; }
  .use-motion .logo-line-after i { right: initial; }
  </style>
</noscript>

</head>

<body itemscope itemtype="http://schema.org/WebPage">
  <div class="container use-motion">
    <div class="headband"></div>

    <header class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-container">
  <div class="site-nav-toggle">
    <div class="toggle" aria-label="切换导航栏">
      <span class="toggle-line toggle-line-first"></span>
      <span class="toggle-line toggle-line-middle"></span>
      <span class="toggle-line toggle-line-last"></span>
    </div>
  </div>

  <div class="site-meta">

    <a href="/" class="brand" rel="start">
      <span class="logo-line-before"><i></i></span>
      <h1 class="site-title">Albert Guo</h1>
      <span class="logo-line-after"><i></i></span>
    </a>
  </div>

  <div class="site-nav-right">
    <div class="toggle popup-trigger">
    </div>
  </div>
</div>




<nav class="site-nav">
  <ul id="menu" class="main-menu menu">
        <li class="menu-item menu-item-home">

    <a href="/" rel="section"><i class="fa fa-home fa-fw"></i>首页</a>

  </li>
        <li class="menu-item menu-item-categories">

    <a href="/categories/" rel="section"><i class="fa fa-bars fa-fw"></i>分类</a>

  </li>
        <li class="menu-item menu-item-about">

    <a href="/about/" rel="section"><i class="fa fa-address-card fa-fw"></i>简介</a>

  </li>
        <li class="menu-item menu-item-webpack">

    <a href="/webpack/" rel="section"><i class="fa fa-bookmark fa-fw"></i>webpack</a>

  </li>
        <li class="menu-item menu-item-javascript">

    <a href="/JavaScript/" rel="section"><i class="fa fa-bookmark fa-fw"></i>ECMAScript 5.1</a>

  </li>
        <li class="menu-item menu-item-ecmascript6">

    <a href="/ECMAScript6/" rel="section"><i class="fa fa-bookmark fa-fw"></i>ECMAScript 6</a>

  </li>
        <li class="menu-item menu-item-react">

    <a href="/React/" rel="section"><i class="fa fa-bookmark fa-fw"></i>React</a>

  </li>
  </ul>
</nav>




</div>
    </header>

    
  <div class="back-to-top">
    <i class="fa fa-arrow-up"></i>
    <span>0%</span>
  </div>


    <main class="main">
      <div class="main-inner">
        <div class="content-wrap">
          
  
  

          <div class="content page posts-expand">
            

    
    
    
    <div class="post-block" lang="zh-CN">
      <header class="post-header">

<h1 class="post-title" itemprop="name headline">ECMAScript6
</h1>

<div class="post-meta">
  

</div>

</header>

      
      
      
      <div class="post-body">
          <h1 id="ES6-简介"><a href="#ES6-简介" class="headerlink" title="ES6 简介"></a>ES6 简介</h1><p>ECMAScript 6.0（以下简称 ES6）是 JavaScript 语言的下一代标准，==已经在2015年6月正式发布。== 它的目标，是使得 JavaScript 语言可以用来编写复杂的大型应用程序，成为企业级开发语言。</p>
<h1 id="let-和-const-命令"><a href="#let-和-const-命令" class="headerlink" title="let 和 const 命令"></a>let 和 const 命令</h1><h2 id="let-命令"><a href="#let-命令" class="headerlink" title="let 命令"></a>let 命令</h2><h3 id="基本用法"><a href="#基本用法" class="headerlink" title="基本用法"></a>基本用法</h3><p>ES6 新增了 <code>let</code> 命令，用来声明变量。它的用法类似于 <code>var</code>，但是所声明的变量，==只在 <code>let</code> 命令所在的代码块内有效。==</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">    <span class="keyword">let</span> a = <span class="number">10</span>;</span><br><span class="line">    <span class="keyword">var</span> b = <span class="number">1</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">a <span class="comment">// ReferenceError:a is not defined.</span></span><br><span class="line">b <span class="comment">// 1</span></span><br></pre></td></tr></table></figure>

<h3 id="不存在变量提升"><a href="#不存在变量提升" class="headerlink" title="不存在变量提升"></a>不存在变量提升</h3><p>==<code>var</code> 命令会发生“变量提升”现象，即变量可以在声明之前使用，值为 <code>undefined</code>。<code>let</code> 命令改变了语法行为，它所声明的变量一定要在声明后使用，否则报错。==</p>
<h3 id="暂时性死区"><a href="#暂时性死区" class="headerlink" title="暂时性死区"></a>暂时性死区</h3><p>==只要块级作用域内存在 <code>let</code> 命令，它所声明的变量就“绑定”这个区域，不再受外部的影响。==</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> tmp = <span class="number">123</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span>(ture)&#123;</span><br><span class="line">    tmp = <span class="string">'abc'</span>;<span class="comment">// ReferenceError</span></span><br><span class="line">    <span class="keyword">let</span> tmp;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>暂时性死区的本质就是，只要一旦进入当前作用域，所要使用的变量就已经存在了，但是不可获取，只有等到声明变量的那一行代码出现，才可以获取和使用该变量。</p>
<h3 id="不允许重复声明"><a href="#不允许重复声明" class="headerlink" title="不允许重复声明"></a>不允许重复声明</h3><p>==<code>let</code> 不允许在相同作用域内，重复声明同一个变量。因此，不能在函数内部重新声明参数。==</p>
<h2 id="块级作用域"><a href="#块级作用域" class="headerlink" title="块级作用域"></a>块级作用域</h2><p>==ES5 只有全局作用域和函数作用域，没有块级作用域，== 造成了很多不合理场景。例如：内层变量可能会覆盖外层变量；用来计数的循环变量（<code>for 循环</code>）泄漏为全局变量。</p>
<p>==ES6 新增了块级作用域。==</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">    <span class="keyword">let</span> a = <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">const</span> b =<span class="number">2</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>==ES6 的块级作用域必须有大括号，如果没有大括号，JavaScript 引擎认为不存在块级作用域。==</p>
<h2 id="const-命令"><a href="#const-命令" class="headerlink" title="const 命令"></a>const 命令</h2><h3 id="基本用法-1"><a href="#基本用法-1" class="headerlink" title="基本用法"></a>基本用法</h3><p>==<code>const</code> 声明一个只读的常量。一旦声明，常量的值就不能改变。==</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> PI = <span class="number">3.14</span>;</span><br><span class="line">PI <span class="comment">// 3.14</span></span><br><span class="line"></span><br><span class="line">PI = <span class="number">3</span>;</span><br><span class="line"><span class="comment">// TypeError:Assignment to constant variable.</span></span><br></pre></td></tr></table></figure>

<p><code>const</code> 声明的变量不得改变值，这意味着，==<code>const</code> 一旦声明变量，就必须立即初始化，不能留到以后赋值。==</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> foo; <span class="comment">// SyntaxError: Missing initializer in const declaration</span></span><br></pre></td></tr></table></figure>

<p><code>const</code> 的作用域与 <code>let</code> 命令相同：==只在声明所在的块级作用域内有效。==</p>
<h3 id="本质"><a href="#本质" class="headerlink" title="本质"></a>本质</h3><p>==<code>const</code> 实际上保证的，并不是变量的值不得改动，而是变量指向的那个内存地址所保存的数据不得改动。对于简单类型的数据（数值、字符串、布尔值），值就保存在变量指向的那个内存地址，因此等同于常量。但对于复合类型的数据（主要是对象和数组），变量指向的内存地址，保存的只是一个指向实际数据的指针，<code>const</code> 只能保证这个指针是固定的（即总是指向另一个固定的地址），至于它指向的数据结构是不是可变的，就完全不能控制了。== 比如 <code>const</code> 声明的对象，依然可以添加属性操作。因此，将一个对象声明为常量必须非常小心。</p>
<h2 id="ES6-声明变量的-6-种方法"><a href="#ES6-声明变量的-6-种方法" class="headerlink" title="ES6 声明变量的 6 种方法"></a>ES6 声明变量的 6 种方法</h2><p>ES5 只有 2 种声明变量的方法：==<code>var、function</code>。==</p>
<p>ES6 一共有 6 种声明变量的方法：==<code>let、const、var、function、import、class</code>。==</p>
<h2 id="顶层对象属性"><a href="#顶层对象属性" class="headerlink" title="顶层对象属性"></a>顶层对象属性</h2><p>顶层对象，==浏览器环境下指的是 <code>window</code> 对象，在 <code>Node</code> 指的是 <code>global</code> 对象。== ES5 中，顶层对象的属性与全局变量是等价的。</p>
<p>顶层对象的属性与全局变量挂钩，被认为是 JavaScript 语言最大的设计败笔之一。ES6 为了改变这一点，一方面规定，==为了保持兼容性，<code>var</code> 命令和 <code>function</code> 命令声明的全局变量，依旧是顶层对象的属性；另一方面规定，<code>let</code> 命令、<code>const</code> 命令、<code>class</code> 命令声明的全局变量，不属于顶层对象。从 ES6 开始，全局变量将逐步与顶层对象的属性脱钩。==</p>
<h1 id="变量的解构赋值"><a href="#变量的解构赋值" class="headerlink" title="变量的解构赋值"></a>变量的解构赋值</h1><p>ES6 允许按照一定模式，从数组和对象中提取值，对变量进行赋值，这被称为解构。</p>
<h2 id="数组解构赋值"><a href="#数组解构赋值" class="headerlink" title="数组解构赋值"></a>数组解构赋值</h2><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> [a,b,c] = [<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>];</span><br><span class="line">a <span class="comment">// 1</span></span><br><span class="line">b <span class="comment">// 2</span></span><br><span class="line">c <span class="comment">//3</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> [foo,[[bar],baz]] = [<span class="number">1</span>,[[<span class="number">2</span>],<span class="number">3</span>]];</span><br><span class="line">foo <span class="comment">// 1</span></span><br><span class="line">bar <span class="comment">// 2</span></span><br><span class="line">baz <span class="comment">// 3</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> [x,,y] = [<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>];</span><br><span class="line">x <span class="comment">// 1</span></span><br><span class="line">y <span class="comment">// 3</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> [head,...tail] = [<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>];</span><br><span class="line">head <span class="comment">// 1</span></span><br><span class="line">tail <span class="comment">// [2,3,4]</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> [x,y,...z] = [<span class="string">'a'</span>];</span><br><span class="line">x <span class="comment">// 'a'</span></span><br><span class="line">y <span class="comment">// undefined</span></span><br><span class="line">z <span class="comment">// []</span></span><br></pre></td></tr></table></figure>

<p>本质上，这种写法属于“模式匹配”，只要等号两边的模式相同，左边的变量就会被赋予对应的值。如果解构不成功，变量的值就等于 <code>undefined</code>。只要某种数据结构具有 Iterator 接口，都可以采用数组形式的解构赋值。</p>
<h2 id="对象的解构赋值"><a href="#对象的解构赋值" class="headerlink" title="对象的解构赋值"></a>对象的解构赋值</h2><p>解构赋值不仅可以用于数组，还可以用于对象。</p>
<p>==对象的解构赋值与数组有一个重要的不同。数组的元素是按次序排列的，变量的取值由它的位置决定；而对象的属性没有次序，变量必须与属性同名，才能取到正确的值。==</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> &#123;foo,bar&#125; = &#123;<span class="attr">foo</span>:<span class="string">'aaa'</span>,<span class="attr">bar</span>:<span class="string">'bbb'</span>&#125;;</span><br><span class="line">foo <span class="comment">// "aaa"</span></span><br><span class="line">bar <span class="comment">// "bbb"</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> &#123;bar,foo&#125; = &#123;<span class="attr">foo</span>:<span class="string">'aaa'</span>,<span class="attr">bar</span>:<span class="string">'bbb'</span>&#125;;</span><br><span class="line">foo <span class="comment">// "aaa"</span></span><br><span class="line">bar <span class="comment">// "bbb"</span></span><br></pre></td></tr></table></figure>

<p>如果变量名与属性名不一致，必须写成下面这样。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> &#123;<span class="attr">foo</span>:baz&#125; = &#123;<span class="attr">foo</span>:<span class="string">'aaa'</span>,<span class="attr">bar</span>:<span class="string">'bbb'</span>&#125;</span><br><span class="line">baz <span class="comment">// "aaa"</span></span><br></pre></td></tr></table></figure>

<h2 id="默认值"><a href="#默认值" class="headerlink" title="默认值"></a>默认值</h2><p>解构赋值允许指定默认值。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> [foo = <span class="literal">true</span>] =[]; foo <span class="comment">// ture</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> [x,y = <span class="string">'b'</span>] = [<span class="string">'a'</span>]; <span class="comment">// x='a',y='b'</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> &#123;x,y = <span class="number">5</span>&#125; = &#123;<span class="attr">x</span>:<span class="number">1</span>&#125;; x <span class="comment">// 1    y // 5</span></span><br></pre></td></tr></table></figure>

<h2 id="字符串的解构赋值"><a href="#字符串的解构赋值" class="headerlink" title="字符串的解构赋值"></a>字符串的解构赋值</h2><p>字符串也可以解构赋值。这时因为此时，字符串被转换成了一个类似数组的对象。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> [a,b,c] = <span class="string">'hello'</span>;</span><br><span class="line">a <span class="comment">// "h"</span></span><br><span class="line">b <span class="comment">// "e"</span></span><br><span class="line">c <span class="comment">// "l"</span></span><br><span class="line">d <span class="comment">// undefined</span></span><br></pre></td></tr></table></figure>

<h2 id="用途"><a href="#用途" class="headerlink" title="用途"></a>用途</h2><p>变量的解构赋值用途很多。</p>
<h3 id="交换变量的值"><a href="#交换变量的值" class="headerlink" title="交换变量的值"></a>交换变量的值</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> x = <span class="number">1</span>;</span><br><span class="line"><span class="keyword">let</span> y = <span class="number">2</span>;</span><br><span class="line"></span><br><span class="line">[x, y] = [y, x];</span><br></pre></td></tr></table></figure>

<h3 id="从函数返回多个值"><a href="#从函数返回多个值" class="headerlink" title="从函数返回多个值"></a>从函数返回多个值</h3><p>函数只能返回一个值，如果要返回多个值，只能将它们放在数组或对象里返回。有了解构赋值，取出这些值就非常方便。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 返回一个数组</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">example</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>];</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">let</span> [a, b, c] = example();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 返回一个对象</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">example</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> &#123;</span><br><span class="line">    foo: <span class="number">1</span>,</span><br><span class="line">    bar: <span class="number">2</span></span><br><span class="line">  &#125;;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">let</span> &#123; foo, bar &#125; = example();</span><br></pre></td></tr></table></figure>

<h3 id="函数参数的定义"><a href="#函数参数的定义" class="headerlink" title="函数参数的定义"></a>函数参数的定义</h3><p>解构赋值可以方便地将一组参数与变量名对应起来。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 参数是一组有次序的值</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">f</span>(<span class="params">[x, y, z]</span>) </span>&#123; ... &#125;</span><br><span class="line">f([<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>]);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 参数是一组无次序的值</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">f</span>(<span class="params">&#123;x, y, z&#125;</span>) </span>&#123; ... &#125;</span><br><span class="line">f(&#123;<span class="attr">z</span>: <span class="number">3</span>, <span class="attr">y</span>: <span class="number">2</span>, <span class="attr">x</span>: <span class="number">1</span>&#125;);</span><br></pre></td></tr></table></figure>

<h3 id="提取-JSON-数据"><a href="#提取-JSON-数据" class="headerlink" title="提取 JSON 数据"></a>提取 <code>JSON</code> 数据</h3><p>解构赋值对提取 <code>JSON</code> 对象中的数据，尤其有用。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> jsonData = &#123;</span><br><span class="line">  id: <span class="number">42</span>,</span><br><span class="line">  status: <span class="string">"OK"</span>,</span><br><span class="line">  data: [<span class="number">867</span>, <span class="number">5309</span>]</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> &#123; id, status, <span class="attr">data</span>: number &#125; = jsonData;</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(id, status, number);</span><br><span class="line"><span class="comment">// 42, "OK", [867, 5309]</span></span><br></pre></td></tr></table></figure>

<h3 id="函数参数的默认值"><a href="#函数参数的默认值" class="headerlink" title="函数参数的默认值"></a>函数参数的默认值</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">jQuery.ajax = <span class="function"><span class="keyword">function</span> (<span class="params">url, &#123;</span></span></span><br><span class="line"><span class="function"><span class="params">  async = true,</span></span></span><br><span class="line"><span class="function"><span class="params">  beforeSend = function (</span>) </span>&#123;&#125;,</span><br><span class="line">  cache = <span class="literal">true</span>,</span><br><span class="line">  complete = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;&#125;,</span><br><span class="line">  crossDomain = <span class="literal">false</span>,</span><br><span class="line">  global = <span class="literal">true</span>,</span><br><span class="line">  <span class="comment">// ... more config</span></span><br><span class="line">&#125; = &#123;&#125;) &#123;</span><br><span class="line">  <span class="comment">// ... do stuff</span></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p>指定参数的默认值，就避免了在函数体内部再写 <code>var foo = config.foo || &#39;default foo&#39;;</code> 这样的语句。</p>
<h3 id="遍历-Map-结构"><a href="#遍历-Map-结构" class="headerlink" title="遍历 Map 结构"></a>遍历 Map 结构</h3><p>任何部署了 <code>Iterator</code> 接口的对象，都可以用 <code>for...of</code> 循环遍历。<code>Map</code> 结构原生支持 <code>Iterator</code> 接口，配合变量的解构赋值，获取键名和键值就非常方便。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> map = <span class="keyword">new</span> <span class="built_in">Map</span>();</span><br><span class="line">map.set(<span class="string">'first'</span>, <span class="string">'hello'</span>);</span><br><span class="line">map.set(<span class="string">'second'</span>, <span class="string">'world'</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">let</span> [key, value] <span class="keyword">of</span> map) &#123;</span><br><span class="line">  <span class="built_in">console</span>.log(key + <span class="string">" is "</span> + value);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// first is hello</span></span><br><span class="line"><span class="comment">// second is world</span></span><br></pre></td></tr></table></figure>

<p>如果只想获取键名，或者只想获取键值，可以写成下面这样。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 获取键名</span></span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">let</span> [key] <span class="keyword">of</span> map) &#123;</span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 获取键值</span></span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">let</span> [,value] <span class="keyword">of</span> map) &#123;</span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="输入模块的指定方法"><a href="#输入模块的指定方法" class="headerlink" title="输入模块的指定方法"></a>输入模块的指定方法</h3><p>加载模块时，往往需要指定输入哪些方法。解构赋值使得输入语句非常清晰。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> &#123; SourceMapConsumer, SourceNode &#125; = <span class="built_in">require</span>(<span class="string">"source-map"</span>);</span><br></pre></td></tr></table></figure>

<h1 id="字符串的扩展"><a href="#字符串的扩展" class="headerlink" title="字符串的扩展"></a>字符串的扩展</h1><h2 id="字符串的遍历器接口"><a href="#字符串的遍历器接口" class="headerlink" title="字符串的遍历器接口"></a>字符串的遍历器接口</h2><p>ES6 为字符串添加了遍历器接口（详见《Iterator》一章），使得字符串可以被 <code>for...of</code> 循环遍历。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span> (<span class="keyword">let</span> codePoint <span class="keyword">of</span> <span class="string">'foo'</span>) &#123;</span><br><span class="line">  <span class="built_in">console</span>.log(codePoint)</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// "f"</span></span><br><span class="line"><span class="comment">// "o"</span></span><br><span class="line"><span class="comment">// "o"</span></span><br></pre></td></tr></table></figure>

<h2 id="模板字符串"><a href="#模板字符串" class="headerlink" title="模板字符串"></a>模板字符串</h2><p>ES6 新增了模板字符串。模板字符串是增强版的字符串，==用反引号（ ` ）表示。== 它可以当作普通的字符串使用，也可以用来定义多行字符串，或者在字符串中嵌入变量。==使用 ${} 可以嵌入变量，JavaScript 表达式。==</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">`User <span class="subst">$&#123;user.name&#125;</span> is not authorized to do <span class="subst">$&#123;action&#125;</span>.`</span></span><br></pre></td></tr></table></figure>

<h2 id="includes-、startsWith-、endsWith"><a href="#includes-、startsWith-、endsWith" class="headerlink" title="includes()、startsWith()、endsWith()"></a>includes()、startsWith()、endsWith()</h2><p>传统上，JavaScript 只有 <code>indexOf</code> 方法，可以用来确定一个字符串是否包含在另一个字符串中。ES6 又提供了三种方法。</p>
<ul>
<li><code>includes()</code>：返回布尔值，表示是否找到了参数字符串。</li>
<li><code>startsWith()</code>：返回布尔值，表示参数字符串是否在源字符串的头部。</li>
<li><code>endsWith()</code>：返回布尔值，表示参数字符串是否在源字符串的尾部。</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> s = <span class="string">'Hello world!'</span>;</span><br><span class="line"></span><br><span class="line">s.startsWith(<span class="string">'Hello'</span>) <span class="comment">// true</span></span><br><span class="line">s.endsWith(<span class="string">'!'</span>) <span class="comment">// true</span></span><br><span class="line">s.includes(<span class="string">'o'</span>) <span class="comment">// true</span></span><br></pre></td></tr></table></figure>

<p>这三个方法都支持第二个参数，表示开始搜索的位置。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> s = <span class="string">'Hello world!'</span>;</span><br><span class="line"></span><br><span class="line">s.startsWith(<span class="string">'world'</span>, <span class="number">6</span>) <span class="comment">// true</span></span><br><span class="line">s.endsWith(<span class="string">'Hello'</span>, <span class="number">5</span>) <span class="comment">// true</span></span><br><span class="line">s.includes(<span class="string">'Hello'</span>, <span class="number">6</span>) <span class="comment">// false</span></span><br></pre></td></tr></table></figure>

<h2 id="repeat"><a href="#repeat" class="headerlink" title="repeat()"></a>repeat()</h2><p><code>repeat</code> 方法返回一个新字符串，表示将原字符串重复 <code>n</code> 次。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">'x'</span>.repeat(<span class="number">3</span>) <span class="comment">// 'xxx'</span></span><br></pre></td></tr></table></figure>

<h1 id="正则的扩展"><a href="#正则的扩展" class="headerlink" title="正则的扩展"></a>正则的扩展</h1><p>略。</p>
<h1 id="数值的扩展"><a href="#数值的扩展" class="headerlink" title="数值的扩展"></a>数值的扩展</h1><h2 id="Number-isFinite-、Number-isNaN"><a href="#Number-isFinite-、Number-isNaN" class="headerlink" title="Number.isFinite()、Number.isNaN()"></a>Number.isFinite()、Number.isNaN()</h2><p>ES6 在 <code>Number</code>对象上，新提供了 <code>Number.isFinite()</code> 和 <code>Number.isNaN()</code> 两个方法。</p>
<p><code>Number.isFinite()</code> 用来检查一个数值是否为有限的（<code>finite</code>），即不是 <code>Infinity</code>。</p>
<p><code>Number.isNaN()</code> 用来检查一个值是否为 <code>NaN</code>。</p>
<h2 id="Number-parseInt-、Number-parseFloat"><a href="#Number-parseInt-、Number-parseFloat" class="headerlink" title="Number.parseInt()、Number.parseFloat()"></a>Number.parseInt()、Number.parseFloat()</h2><p>ES6 将全局方法 <code>parseInt()</code> 和 <code>parseFloat()</code> 移植到 <code>Number</code> 对象上面，行为用法完全不变。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ES5的写法</span></span><br><span class="line"><span class="built_in">parseInt</span>(<span class="string">'12.34'</span>) <span class="comment">// 12</span></span><br><span class="line"><span class="built_in">parseFloat</span>(<span class="string">'123.45#'</span>) <span class="comment">// 123.45</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// ES6的写法</span></span><br><span class="line"><span class="built_in">Number</span>.parseInt(<span class="string">'12.34'</span>) <span class="comment">// 12</span></span><br><span class="line"><span class="built_in">Number</span>.parseFloat(<span class="string">'123.45#'</span>) <span class="comment">// 123.45</span></span><br></pre></td></tr></table></figure>

<h2 id="Number-isInteger"><a href="#Number-isInteger" class="headerlink" title="Number.isInteger()"></a>Number.isInteger()</h2><p><code>Number.isInteger()</code> 用来判断一个数值是否为整数。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">Number.isInteger(25) &#x2F;&#x2F; true</span><br><span class="line">Number.isInteger(25.1) &#x2F;&#x2F; false</span><br><span class="line">Number.isInteger(25) &#x2F;&#x2F; true</span><br><span class="line">Number.isInteger(25.0) &#x2F;&#x2F; true</span><br></pre></td></tr></table></figure>

<h1 id="函数的扩展"><a href="#函数的扩展" class="headerlink" title="函数的扩展"></a>函数的扩展</h1><h2 id="函数参数默认值"><a href="#函数参数默认值" class="headerlink" title="函数参数默认值"></a>函数参数默认值</h2><p>ES6 允许为函数的参数设置默认值，即直接写在参数定义的后面。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">log</span>(<span class="params"> x, y = <span class="string">'world'</span></span>)</span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log( x, y);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">log(<span class="string">'hello'</span>) <span class="comment">// hello world</span></span><br><span class="line">log(<span class="string">'hello'</span>,<span class="string">'China'</span>) <span class="comment">// hello China</span></span><br></pre></td></tr></table></figure>

<p>==参数变量是默认声明的，所以不能用 <code>let</code> 或 <code>const</code> 再次声明。==</p>
<p>定义了默认值的参数，应该是函数的尾参数。==如果非尾参数设置默认值，实际上这个参数是没法省略的。==</p>
<h2 id="rest-参数"><a href="#rest-参数" class="headerlink" title="rest 参数"></a>rest 参数</h2><p>ES6 引入了 <code>rest</code> 参数（形式为 <code>...变量名</code> ），用于获取函数的多余参数，这样就不需要使用 <code>arguments</code> 对象了。<code>rest</code> 参数搭配的变量是一个数组，该变量是将多余的参数放入数组中。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">add</span>(<span class="params">...values</span>)</span>&#123;</span><br><span class="line">    <span class="keyword">let</span> sum = <span class="number">0</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">for</span>(<span class="keyword">var</span> val <span class="keyword">of</span> values)&#123;</span><br><span class="line">        sum += val;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> sum;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">add(<span class="number">2</span>,<span class="number">5</span>,<span class="number">3</span>) <span class="comment">// 10</span></span><br></pre></td></tr></table></figure>

<p>==<code>arguments</code> 对象不是数组，而是一个类似数组的对象。所以为了使用数组的方法，必须使用 <code>Array.prototype.slice.call</code> 先将其转为数组。而 <code>rest</code> 参数就是一个真正的数组，数组特有的方法都可以使用。==</p>
<p>==<code>rest</code> 参数之后不能再有其他参数（即只能是最后一个参数），否则会报错。==</p>
<h2 id="箭头函数"><a href="#箭头函数" class="headerlink" title="箭头函数"></a>箭头函数</h2><p>ES6 允许使用“箭头”（=&gt;）定义函数。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> f = <span class="function"><span class="params">v</span> =&gt;</span> v;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 等同于</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">f</span>(<span class="params">v</span>)</span>&#123;</span><br><span class="line">    <span class="keyword">return</span> v;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>如果箭头函数不需要参数或需要多个参数，就使用一个圆括号代表参数部分。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> f = <span class="function"><span class="params">()</span> =&gt;</span> <span class="number">5</span>;</span><br><span class="line"><span class="comment">// 等同于</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">f</span>(<span class="params"></span>)</span>&#123; <span class="keyword">return</span> <span class="number">5</span> &#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> sum = <span class="function">(<span class="params"> n1, n2 </span>) =&gt;</span> n1 + n2;</span><br><span class="line"><span class="comment">// 等同于</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">sum</span>(<span class="params"> n1, n2 </span>)</span>&#123; <span class="keyword">return</span> n1 + n2 &#125;;</span><br></pre></td></tr></table></figure>

<p>如果箭头函数的代码块部分多余一条语句，就要使用大括号将它们括起来，并且使用 <code>return</code> 语句返回。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> sum = <span class="function">(<span class="params"> num1, num2 </span>) =&gt;</span> &#123; <span class="keyword">return</span> num1 + num2; &#125;</span><br></pre></td></tr></table></figure>

<p>由于大括号被解析为代码块，所以==如果箭头函数返回直接一个对象，必须在对象外面加上括号，否则会报错。==</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 报错</span></span><br><span class="line"><span class="keyword">let</span> getTempItem = <span class="function"><span class="params">id</span> =&gt;</span> &#123;<span class="attr">id</span>:id,<span class="attr">name</span>:<span class="string">'Temp'</span>&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 不报错</span></span><br><span class="line"><span class="keyword">let</span> getTempItem = <span class="function"><span class="params">id</span> =&gt;</span> (&#123;<span class="attr">id</span>:id,<span class="attr">name</span>:<span class="string">'Temp'</span>&#125;);</span><br></pre></td></tr></table></figure>

<h3 id="箭头函数注意点："><a href="#箭头函数注意点：" class="headerlink" title="箭头函数注意点："></a>箭头函数注意点：</h3><ul>
<li>==函数体内的 <code>this</code> 对象，就是定义时所在的对象，而不是使用时所在的对象。==</li>
<li>==不可以当作构造函数，也就是说，不可以使用 <code>new</code> 命令，== 否则会抛出一个错误。</li>
<li>==不可以使用 <code>arguments</code> 对象，该对象在函数体内不存在。如果要用，可以使用 <code>rest</code> 参数代替。==</li>
<li>不可以使用 <code>yield</code> 命令，因此==箭头函数不能用作 <code>Genterator</code> 函数。==</li>
</ul>
<h3 id="this-指向"><a href="#this-指向" class="headerlink" title="this 指向"></a>this 指向</h3><p>==<code>this</code> 对象的指向是可变的，但是在箭头函数中，它是固定的。<code>this</code> 指向的固定话，并不是因为箭头函数内部有绑定 <code>this</code> 的机制，实际原因是箭头函数根本没有自己的 <code>this</code>，导致内部的 <code>this</code> 就是外层代码块的 <code>this</code>。正因为它没有 <code>this</code> ，所以也就不能用作构造函数。==</p>
<h3 id="不适合场合"><a href="#不适合场合" class="headerlink" title="不适合场合"></a>不适合场合</h3><p>由于箭头函数使得 <code>this</code> 从“动态”变成“静态”，下面两个场合不应该使用箭头函数。</p>
<h4 id="场合一：定义对象的方法，且该方法内部包括-this"><a href="#场合一：定义对象的方法，且该方法内部包括-this" class="headerlink" title="场合一：定义对象的方法，且该方法内部包括 this"></a>场合一：定义对象的方法，且该方法内部包括 <code>this</code></h4><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 错误示范</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> cat = &#123;</span><br><span class="line">    lives:<span class="number">9</span>,</span><br><span class="line">    jumps:<span class="function"><span class="params">()</span>=&gt;</span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.lives--;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="场合二：需要动态-this-的时候，也不应该使用箭头函数"><a href="#场合二：需要动态-this-的时候，也不应该使用箭头函数" class="headerlink" title="场合二：需要动态 this 的时候，也不应该使用箭头函数"></a>场合二：需要动态 this 的时候，也不应该使用箭头函数</h4><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 错误示范</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> button = <span class="built_in">document</span>.getElementById(<span class="string">'press'</span>);</span><br><span class="line">button.addEventListener(<span class="string">'click'</span>,()=&gt;&#123;</span><br><span class="line">    <span class="keyword">this</span>.classList.toggle(<span class="string">'on'</span>);</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>

<h2 id="尾调用优化"><a href="#尾调用优化" class="headerlink" title="尾调用优化"></a>尾调用优化</h2><p>尾调用是指某个函数的最后一步是调用另一个函数。</p>
<p>函数调用会在内存形成一个“调用记录”，又称“调用帧”（<code>call frame</code>），保存调用位置和内部变量等信息。如果在函数 A 的内部调用函数 B，那么在 A 的调用帧上方，还会形成一个 B 的调用帧。等到 B 运行结束，将结果返回到 A，B 的调用帧才会消失。如果函数 B 内部还调用函数 C，那就还有一个 C 的调用帧，以此类推。所有的调用帧，就形成一个“调用栈”（<code>call stack</code>）。</p>
<p>尾调用由于是函数的最后一步操作，所以不需要保留外层函数的调用帧，因为调用位置、内部变量等信息都不会再用到了，只要直接用内层函数的调用帧，取代外层函数的调用帧就可以了。</p>
<h1 id="数组的扩展"><a href="#数组的扩展" class="headerlink" title="数组的扩展"></a>数组的扩展</h1><h2 id="扩展运算符"><a href="#扩展运算符" class="headerlink" title="扩展运算符"></a>扩展运算符</h2><p>扩展运算符是三个点（<code>...</code>）。它好比 <code>rest</code> 参数的逆运算，将一个数组转换为用逗号分隔的参数序列。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">console</span>.log(...[<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>]) <span class="comment">// 1 2 3</span></span><br><span class="line"><span class="built_in">console</span>.log(<span class="number">1</span>,<span class="number">2</span>,...[<span class="number">3</span>,<span class="number">4</span>],<span class="number">5</span>) <span class="comment">// 1 2 3 4 5</span></span><br></pre></td></tr></table></figure>

<p>由于扩展运算符可以展开数组，所以不再需要 <code>apply</code> 方法，将数组转为函数的参数了。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// ES5 的写法</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">f</span>(<span class="params"> x, y, z </span>)</span>&#123;</span><br><span class="line">    ...</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> args = [<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>];</span><br><span class="line">f.apply(<span class="literal">null</span>,args);</span><br><span class="line"></span><br><span class="line"><span class="comment">// ES6 的写法</span></span><br><span class="line">function args = [1,2,3];</span><br><span class="line">f(...args)</span><br></pre></td></tr></table></figure>

<h2 id="扩展运算符的应用"><a href="#扩展运算符的应用" class="headerlink" title="扩展运算符的应用"></a>扩展运算符的应用</h2><h3 id="复制数组"><a href="#复制数组" class="headerlink" title="复制数组"></a>复制数组</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> a1 = [<span class="number">1</span>,<span class="number">2</span>];</span><br><span class="line"><span class="keyword">const</span> a2 = [...a1];</span><br></pre></td></tr></table></figure>

<h3 id="合并数组"><a href="#合并数组" class="headerlink" title="合并数组"></a>合并数组</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> a1 = [<span class="string">'a'</span>,<span class="string">'b'</span>];</span><br><span class="line"><span class="keyword">const</span> a2 = [<span class="string">'c'</span>,<span class="string">'d'</span>];</span><br><span class="line"></span><br><span class="line">[...a1,...a2]; <span class="comment">// ['a','b','c','d'];</span></span><br></pre></td></tr></table></figure>

<h3 id="与解构赋值相结合"><a href="#与解构赋值相结合" class="headerlink" title="与解构赋值相结合"></a>与解构赋值相结合</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> [first,...rest] = [<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>,<span class="number">5</span>];</span><br><span class="line">first <span class="comment">// 1</span></span><br><span class="line">rest <span class="comment">// [2,3,4,5]</span></span><br></pre></td></tr></table></figure>

<h3 id="字符串"><a href="#字符串" class="headerlink" title="字符串"></a>字符串</h3><p>扩展运算符可以将字符串转为真正的数组。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">[...<span class="string">'hello'</span>]</span><br><span class="line"><span class="comment">// ['h','e','l','l','o']</span></span><br></pre></td></tr></table></figure>

<h3 id="实现了-Iterator-接口的对象"><a href="#实现了-Iterator-接口的对象" class="headerlink" title="实现了 Iterator 接口的对象"></a>实现了 Iterator 接口的对象</h3><p>任何定义了遍历器（Iterator）接口的对象，都可以用扩展运算符转为真正的数组。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> nodeList = <span class="built_in">document</span>.querySelectorAll(<span class="string">'div'</span>);</span><br><span class="line"><span class="keyword">let</span> array = [...nodeList]</span><br></pre></td></tr></table></figure>

<h2 id="Array-from"><a href="#Array-from" class="headerlink" title="Array.from()"></a>Array.from()</h2><p><code>Array.from</code> 方法用于将两类对象转为真正的数组：==类似数组的对象和可遍历的对象（包括 ES6 新增的数据解构 Set 和 Map）。==</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> arrayLike = &#123;</span><br><span class="line">    <span class="string">'0'</span>:<span class="string">'a'</span>,</span><br><span class="line">    <span class="string">'1'</span>:<span class="string">'b'</span>,</span><br><span class="line">    <span class="string">'2'</span>:<span class="string">'c'</span>,</span><br><span class="line">    length:<span class="number">3</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// ES5 的写法</span></span><br><span class="line"><span class="keyword">var</span> arr1 = [].slice.call(arrayLike); <span class="comment">// ['a','b','c']</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// ES6 的写法</span></span><br><span class="line"><span class="keyword">var</span> arr2 = <span class="built_in">Array</span>.from(arrayLike); <span class="comment">// ['a','b','c']</span></span><br></pre></td></tr></table></figure>

<h2 id="Array-of（）"><a href="#Array-of（）" class="headerlink" title="Array.of（）"></a>Array.of（）</h2><p><code>Array.of</code> 方法用于将一组值，转换为数组。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">Array</span>.of(<span class="number">2</span>,<span class="number">3</span>,<span class="number">44</span>) <span class="comment">// [2,3,44]</span></span><br><span class="line"><span class="built_in">Array</span>.of(<span class="number">2</span>) <span class="comment">// [2]</span></span><br></pre></td></tr></table></figure>

<h2 id="copyWithin"><a href="#copyWithin" class="headerlink" title="copyWithin()"></a>copyWithin()</h2><p>数组实例的 <code>copyWithIn()</code> 方法，在当前数组内部，将指定位置的成员复制到其他位置（会覆盖原有成员），然后返回当前的数组。也就是说，使用这个方法，会修改当前数组。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">Array</span>.prototype.copyWithin(target,start = <span class="number">0</span>,end = <span class="keyword">this</span>.length)</span><br></pre></td></tr></table></figure>

<p>它接收三个参数：</p>
<ul>
<li><code>target</code>（必须）：从该位置开始替换数据。如果为负值，表示倒数。</li>
<li><code>start</code>（可选）：从该位置开始读取数据，默认为 0 .如果为负值，表示从末尾开始计算。</li>
<li><code>end</code>（可选）：到该位置前停止读取数据，默认等于数组长度。如果为负值，表示从末尾开始计算。</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>,<span class="number">5</span>].copyWithin(<span class="number">0</span>,<span class="number">3</span>) <span class="comment">// [4,5,3,4,5]</span></span><br></pre></td></tr></table></figure>

<h2 id="find-、findIndex"><a href="#find-、findIndex" class="headerlink" title="find()、findIndex()"></a>find()、findIndex()</h2><p>数组实例的 <code>find</code> 方法，用于找出第一个符合条件的数组成员。它的参数是一个回调函数，所有数组成员依次执行该回调函数，直到找到第一个返回值为 <code>true</code> 的成员，然后返回该成员。如果没有符合条件的成员，则返回 <code>undefined</code>。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">[<span class="number">1</span>,<span class="number">4</span>,<span class="number">-5</span>,<span class="number">10</span>].find(<span class="function">(<span class="params">n</span>)=&gt;</span> n &lt; <span class="number">0</span>) <span class="comment">// -5</span></span><br><span class="line"></span><br><span class="line">[<span class="number">1</span>,<span class="number">5</span>,<span class="number">10</span>,<span class="number">15</span>].find(<span class="function"><span class="keyword">function</span>(<span class="params">value,index,arr</span>)</span>&#123;</span><br><span class="line">    <span class="keyword">return</span> value &gt; <span class="number">9</span>;</span><br><span class="line">&#125;) <span class="comment">// 10</span></span><br></pre></td></tr></table></figure>

<p>数组实例的 <code>findIndex</code> 方法的用法与 <code>find</code> 方法非常类似，返回第一个符合条件的数组成员的位置，如果所有成员都不符合条件，则返回 <code>-1</code>。</p>
<h2 id="includes"><a href="#includes" class="headerlink" title="includes()"></a>includes()</h2><p><code>Array.prorotype.includes</code> 方法返回一个布尔值，表示某个数组是否包含给定的值，与字符串的 <code>includes</code> 方法类似。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">[<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>].includes(<span class="number">2</span>)     <span class="comment">// true</span></span><br><span class="line">[<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>].includes(<span class="number">4</span>)     <span class="comment">// false</span></span><br><span class="line">[<span class="number">1</span>, <span class="number">2</span>, <span class="literal">NaN</span>].includes(<span class="literal">NaN</span>)    <span class="comment">// true</span></span><br></pre></td></tr></table></figure>

<h1 id="对象的扩展"><a href="#对象的扩展" class="headerlink" title="对象的扩展"></a>对象的扩展</h1><h2 id="属性的简洁表示"><a href="#属性的简洁表示" class="headerlink" title="属性的简洁表示"></a>属性的简洁表示</h2><p>ES6 允许大括号里面，直接写入变量和函数，作为对象的属性和方法。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> foo = <span class="string">'bar'</span>;</span><br><span class="line"><span class="keyword">const</span> baz = &#123; foo &#125;;</span><br><span class="line">baz <span class="comment">// &#123;foo:'bar'&#125;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 等同于</span></span><br><span class="line"><span class="keyword">const</span> baz = &#123; <span class="attr">foo</span>: foo &#125;</span><br></pre></td></tr></table></figure>

<p>除了属性简写，方法也可以简写。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">cosnt o = &#123;</span><br><span class="line">    method()&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">"Hello"</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 等同于</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> o = &#123;</span><br><span class="line">    method:<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">        retrun <span class="string">"Hello"</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>CommonJS 模块输出一组变量，就非常适合使用简洁的写法。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = &#123; getItem, setItem, clear&#125;;</span><br><span class="line"><span class="comment">// 等同于</span></span><br><span class="line"><span class="built_in">module</span>.exports = &#123;</span><br><span class="line">    getItem:getItem,</span><br><span class="line">    setItem:setItem,</span><br><span class="line">    clear:clear</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="属性的可枚举性"><a href="#属性的可枚举性" class="headerlink" title="属性的可枚举性"></a>属性的可枚举性</h2><p>对象的每个属性都有一个描述对象，用来控制该属性的行为。<code>Object.getOwnPropertyDescriptor</code> 方法可以获取该属性的描述对象。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> obj = &#123; <span class="attr">foo</span>: <span class="number">123</span> &#125;;</span><br><span class="line"><span class="built_in">Object</span>.getOwnPropertyDescriptor(obj, <span class="string">'foo'</span>)</span><br><span class="line"><span class="comment">//  &#123;</span></span><br><span class="line"><span class="comment">//    value: 123,</span></span><br><span class="line"><span class="comment">//    writable: true,</span></span><br><span class="line"><span class="comment">//    enumerable: true,</span></span><br><span class="line"><span class="comment">//    configurable: true</span></span><br><span class="line"><span class="comment">//  &#125;</span></span><br></pre></td></tr></table></figure>

<p>描述对象的 <code>enumerable</code> 属性，称为“可枚举性”，如果该属性为  <code>false</code>，就表示某些操作会忽略当前属性。</p>
<p>目前，有四个操作会忽略 <code>enumberable</code> 为 <code>false</code> 的属性。</p>
<ol>
<li><code>for ... in</code> 循环：只遍历自身和继承的可枚举属性。</li>
<li><code>Object.keys()</code>：返回对象自身的所有可枚举的属性的键名。</li>
<li><code>JSON.stringfy()</code>：只串行对象自身的可枚举的属性。</li>
<li><code>Object.assing()</code>：忽略 <code>enumerable</code> 为 <code>false</code> 的属性，只拷贝对象自身的可枚举的属性。</li>
</ol>
<p>==ES6 规定，所有的 <code>Class</code> 的原型的方法都是不可枚举的。==</p>
<p>尽量不要使用 <code>for ... in</code> 循环，而用 <code>Object.keys()</code>代替。</p>
<h2 id="属性遍历"><a href="#属性遍历" class="headerlink" title="属性遍历"></a>属性遍历</h2><p>ES6 一共有 5 种方法可以遍历对象的属性：</p>
<ol>
<li><code>for ... in</code>：循环遍历对象自身和继承的可枚举属性（不含 <code>Symbol</code> 属性）。</li>
<li><code>Object.keys(obj)</code>：返回一个数组，包含对象自身的（不含继承的）所有可枚举属性（不含 <code>Symbol</code> 属性）的键名。</li>
<li><code>Object.getOwnPropertyNames(obj)</code>：返回一个数组，包含对象自身的所有属性（不含 <code>Symbol</code> 属性，但是包括不可枚举的属性）的键名。</li>
<li><code>Object.getOwnPropertySymbols(obj)</code>：返回一个数组，包含对象自身的所有 <code>Symbol</code> 属性的键名。</li>
<li><code>Reflect.ownKeys(obj)</code>：返回一个数组，包含对象自身的所有键名，不管键名是 <code>Symbole</code> 或字符串，也不管是否可以枚举。</li>
</ol>
<h2 id="super-关键字"><a href="#super-关键字" class="headerlink" title="super 关键字"></a>super 关键字</h2><p><code>this</code> 关键字总是指向函数所在的当前对象，==ES6 又新增了另一个类似的关键字 <code>super</code>，指向当前对象的原型对象。==</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> proto = &#123;</span><br><span class="line">  foo: <span class="string">'hello'</span></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> obj = &#123;</span><br><span class="line">  foo: <span class="string">'world'</span>,</span><br><span class="line">  find() &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">super</span>.foo;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="built_in">Object</span>.setPrototypeOf(obj, proto);</span><br><span class="line">obj.find() <span class="comment">// "hello"</span></span><br></pre></td></tr></table></figure>

<p>==<code>super</code> 关键字表示原型对象时，只能用在对象的方法之中，用在其他地方都会报错。目前，只有对象方法的简写可以让 JavaScript 引擎确认，定义的是对象的方法。==</p>
<h2 id="链判断运算符"><a href="#链判断运算符" class="headerlink" title="链判断运算符"></a>链判断运算符</h2><p>编程实务中，如果读取对象内部的某个属性，往往需要判断一下该对象是否存在。比如，要读取 <code>message.body.user.firstName</code>，安全的写法是写成下面这样。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> firstName = (message</span><br><span class="line">  &amp;&amp; message.body</span><br><span class="line">  &amp;&amp; message.body.user</span><br><span class="line">  &amp;&amp; message.body.user.firstName) || <span class="string">'default'</span>;</span><br></pre></td></tr></table></figure>

<p>或者使用三元运算符?:，判断一个对象是否存在。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> fooInput = myForm.querySelector(<span class="string">'input[name=foo]'</span>)</span><br><span class="line"><span class="keyword">const</span> fooValue = fooInput ? fooInput.value : <span class="literal">undefined</span></span><br></pre></td></tr></table></figure>

<p>这样的层层判断非常麻烦，因此 ES2020 引入了“链判断运算符”（optional chaining operator）<code>?.</code>，简化上面的写法。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> firstName = message?.body?.user?.firstName || <span class="string">'default'</span>;</span><br><span class="line"><span class="keyword">const</span> fooValue = myForm.querySelector(<span class="string">'input[name=foo]'</span>)?.value</span><br></pre></td></tr></table></figure>

<p>上面代码使用了 <code>?.</code> 运算符，==直接在链式调用的时候判断，左侧的对象是否为 <code>null</code> 或 <code>undefined</code>。如果是的，就不再往下运算，而是返回 <code>undefined</code>。==</p>
<p>链判断运算符有三种用法。</p>
<ul>
<li><code>obj?.prop</code> // 对象属性</li>
<li><code>obj?.[expr]</code> // 同上</li>
<li><code>func?.(...args)</code> // 函数或对象方法的调用</li>
</ul>
<p>下面是判断对象方法是否存在，如果存在就立即执行的例子。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">iterator.return?.()</span><br></pre></td></tr></table></figure>

<h2 id="Null-判断运算符"><a href="#Null-判断运算符" class="headerlink" title="Null 判断运算符"></a>Null 判断运算符</h2><p>读取对象属性的时候，如果某个属性的值是 <code>null</code> 或 <code>undefined</code>，有时候需要为它们指定默认值。常见做法是通过 <code>||</code> 运算符指定默认值。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> headerText = response.settings.headerText || <span class="string">'Hello, world!'</span>;</span><br><span class="line"><span class="keyword">const</span> animationDuration = response.settings.animationDuration || <span class="number">300</span>;</span><br><span class="line"><span class="keyword">const</span> showSplashScreen = response.settings.showSplashScreen || <span class="literal">true</span>;</span><br></pre></td></tr></table></figure>

<p>上面的三行代码都通过 <code>||</code> 运算符指定默认值，但是这样写是错的。开发者的原意是，只要属性的值为 <code>null</code> 或 <code>undefined</code>，默认值就会生效，但是属性的值如果为 <code>空字符串</code> 或 <code>false</code> 或 <code>0</code> ，默认值也会生效。</p>
<p>为了避免这种情况，ES2020 引入了一个新的 Null 判断运算符 <code>??</code>。它的行为类似 <code>||</code> ，但是只有运算符左侧的值为 <code>null</code>  或 <code>undefined</code> 时，才会返回右侧的值。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> headerText = response.settings.headerText ?? <span class="string">'Hello, world!'</span>;</span><br><span class="line"><span class="keyword">const</span> animationDuration = response.settings.animationDuration ?? <span class="number">300</span>;</span><br><span class="line"><span class="keyword">const</span> showSplashScreen = response.settings.showSplashScreen ?? <span class="literal">true</span>;</span><br></pre></td></tr></table></figure>

<p>上面代码中，默认值只有在属性值为 <code>null</code> 或 <code>undefined</code> 时，才会生效。</p>
<p>这个运算符的一个目的，就是跟链判断运算符 <code>?.</code> 配合使用，为 <code>null</code> 或 <code>undefined</code> 的值设置默认值。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> animationDuration = response.settings?.animationDuration ?? <span class="number">300</span>;</span><br></pre></td></tr></table></figure>

<p>上面代码中，<code>response.settings</code> 如果是 <code>null</code> 或 <code>undefined</code>，就会返回默认值 <code>300</code>。</p>
<p>这个运算符很适合判断函数参数是否赋值。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Component</span>(<span class="params">props</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">const</span> enable = props.enabled ?? <span class="literal">true</span>;</span><br><span class="line">  <span class="comment">// …</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>上面代码判断 <code>props</code> 参数的 <code>enabled</code> 属性是否赋值，等同于下面的写法。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Component</span>(<span class="params">props</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">const</span> &#123;</span><br><span class="line">    enabled: enable = <span class="literal">true</span>,</span><br><span class="line">  &#125; = props;</span><br><span class="line">  <span class="comment">// …</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><code>??</code> 有一个运算优先级问题，它与 <code>&amp;&amp;</code> 和 <code>||</code> 的优先级孰高孰低。现在的规则是，如果多个逻辑运算符一起使用，必须用括号表明优先级，否则会报错。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 报错</span></span><br><span class="line">lhs &amp;&amp; middle ?? rhs</span><br><span class="line">lhs ?? middle &amp;&amp; rhs</span><br><span class="line">lhs || middle ?? rhs</span><br><span class="line">lhs ?? middle || rhs</span><br></pre></td></tr></table></figure>

<p>上面四个表达式都会报错，必须加入表明优先级的括号。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">(lhs &amp;&amp; middle) ?? rhs;</span><br><span class="line">lhs &amp;&amp; (middle ?? rhs);</span><br><span class="line"></span><br><span class="line">(lhs ?? middle) &amp;&amp; rhs;</span><br><span class="line">lhs ?? (middle &amp;&amp; rhs);</span><br><span class="line"></span><br><span class="line">(lhs || middle) ?? rhs;</span><br><span class="line">lhs || (middle ?? rhs);</span><br><span class="line"></span><br><span class="line">(lhs ?? middle) || rhs;</span><br><span class="line">lhs ?? (middle || rhs);</span><br></pre></td></tr></table></figure>

<h1 id="对象的新增方法"><a href="#对象的新增方法" class="headerlink" title="对象的新增方法"></a>对象的新增方法</h1><h2 id="Object-is"><a href="#Object-is" class="headerlink" title="Object.is()"></a>Object.is()</h2><p>==ES5 比较两个值是否相等，只有两个运算符：相等运算符（<code>==</code>）和严格相等运算符（<code>===</code>）。它们都有缺点，前者会自动转换数据类型，后者的 <code>NaN</code> 不等于自身，以及 <code>+0</code> 等于 <code>-0</code>。JavaScript 缺乏一种运算，在所有环境中，只要两个值是一样的，它们就应该相等。==</p>
<p>==<code>Object.is</code> 用来比较两个值是否严格相等，与严格比较运算符（<code>===</code>）的行为基本一致。==</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">Object</span>.is(<span class="string">'foo'</span>,<span class="string">'foo'</span>); <span class="comment">// true</span></span><br><span class="line"><span class="built_in">Object</span>.is(&#123;&#125;,&#123;&#125;) <span class="comment">// false</span></span><br></pre></td></tr></table></figure>

<p>==不同之处只有两个：一是 <code>+0</code> 不等于 <code>-0</code>，二是 <code>NaN</code> 等于自身。==</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">+<span class="number">0</span> === <span class="number">-0</span> <span class="comment">// true</span></span><br><span class="line"><span class="literal">NaN</span> === <span class="literal">NaN</span> <span class="comment">// false</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">Object</span>.is(+<span class="number">0</span>,<span class="number">-0</span>) <span class="comment">// false</span></span><br><span class="line"><span class="built_in">Object</span>.is(<span class="literal">NaN</span>,<span class="literal">NaN</span>) <span class="comment">// true</span></span><br></pre></td></tr></table></figure>

<h2 id="Object-assign"><a href="#Object-assign" class="headerlink" title="Object.assign()"></a>Object.assign()</h2><p><code>Object.assign</code> 方法用于对象的合并，将源对象（<code>source</code>）的所有可枚举属性，复制到目标对象（<code>target</code>）。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">cont target = &#123;<span class="attr">a</span>:<span class="number">1</span>&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> source1 = &#123;<span class="attr">b</span>:<span class="number">2</span>&#125;;</span><br><span class="line"><span class="keyword">const</span> source2 = &#123;<span class="attr">c</span>:<span class="number">3</span>&#125;;</span><br><span class="line"></span><br><span class="line"><span class="built_in">Object</span>.assign(target,source1,source2);</span><br><span class="line">target <span class="comment">// &#123;a:1,b:2,c:3&#125;</span></span><br></pre></td></tr></table></figure>

<p><code>Object.assign</code> 方法的第一个参数是目标对象，后面的参数都是源对象。</p>
<p>注意，如果目标对象与源对象有同名属性，或多个源对象有同名属性，则后面的属性会==覆盖前面的属性。==</p>
<h3 id="使用注意点"><a href="#使用注意点" class="headerlink" title="使用注意点"></a>使用注意点</h3><h4 id="（1）潜拷贝"><a href="#（1）潜拷贝" class="headerlink" title="（1）潜拷贝"></a>（1）潜拷贝</h4><p><code>Object.assign</code> 方法实行的是==浅拷贝，而不是深拷贝。== 也就是说，如果源对象某个属性的值是对象，那么目标对象拷贝得道的是这个对象的引用。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> o1 = &#123;<span class="attr">a</span>:&#123;<span class="attr">b</span>:<span class="number">1</span>&#125;&#125;;</span><br><span class="line"><span class="keyword">const</span> o2 = <span class="built_in">Object</span>.assign(&#123;&#125;,o1);</span><br><span class="line"></span><br><span class="line">o1.a.b = <span class="number">2</span>;</span><br><span class="line">ob.a.b <span class="comment">// 2</span></span><br></pre></td></tr></table></figure>

<h4 id="（2）同名属性的替换"><a href="#（2）同名属性的替换" class="headerlink" title="（2）同名属性的替换"></a>（2）同名属性的替换</h4><p>对于嵌套的对象，一旦遇到同名属性，<code>Object.assign</code> 的处理方法是==替换，而不是添加。==</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> target = &#123;<span class="attr">a</span>:&#123;<span class="attr">b</span>:<span class="string">'c'</span>,<span class="attr">d</span>:<span class="string">'e'</span>&#125;&#125;;</span><br><span class="line"><span class="keyword">const</span> source = &#123;<span class="attr">a</span>:&#123;<span class="attr">b</span>:<span class="string">'hello'</span>&#125;&#125;;</span><br><span class="line"><span class="built_in">Object</span>.assign(target,source) <span class="comment">// &#123;a:&#123;b:'hello'&#125;&#125;</span></span><br></pre></td></tr></table></figure>

<h4 id="（3）数组的处理"><a href="#（3）数组的处理" class="headerlink" title="（3）数组的处理"></a>（3）数组的处理</h4><p>可以用来处理数组，但是会把数组视为对象。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">Object</span>.assign([<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>],[<span class="number">4</span>,<span class="number">5</span>]) <span class="comment">// [4,5,3]</span></span><br></pre></td></tr></table></figure>

<p>上面代码中，<code>Object.assign</code> 把数组视为属性名为 <code>0、1、2</code> 的对象，因此源数组的 <code>0</code> 号属性 <code>4</code> 覆盖了目标数组的 <code>0</code> 号属性 <code>1</code>。</p>
<h4 id="（4）取值函数的处理"><a href="#（4）取值函数的处理" class="headerlink" title="（4）取值函数的处理"></a>（4）取值函数的处理</h4><p><code>Object.assign</code> 只能进行值的复制，如果要复制的值是一个取值函数，那么将会求值后再复制。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> source = &#123;</span><br><span class="line">  <span class="keyword">get</span> foo() &#123; <span class="keyword">return</span> <span class="number">1</span> &#125;</span><br><span class="line">&#125;;</span><br><span class="line"><span class="keyword">const</span> target = &#123;&#125;;</span><br><span class="line"></span><br><span class="line"><span class="built_in">Object</span>.assign(target, source)</span><br><span class="line"><span class="comment">// &#123; foo: 1 &#125;</span></span><br></pre></td></tr></table></figure>

<h4 id="（5）undefind、null"><a href="#（5）undefind、null" class="headerlink" title="（5）undefind、null"></a>（5）undefind、null</h4><p>由于 <code>undefined</code> 和 <code>null</code> 无法转成对象，所以==如果它们作为参数，就会报错。==</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">Object</span>.assign(<span class="literal">undefined</span>) <span class="comment">// 报错</span></span><br><span class="line"><span class="built_in">Object</span>.assign(<span class="literal">null</span>) <span class="comment">// 报错</span></span><br></pre></td></tr></table></figure>

<h3 id="常见用途"><a href="#常见用途" class="headerlink" title="常见用途"></a>常见用途</h3><p><code>Object.assign</code> 方法有很多用处。</p>
<h4 id="（1）为对象添加属性"><a href="#（1）为对象添加属性" class="headerlink" title="（1）为对象添加属性"></a>（1）为对象添加属性</h4><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Point</span> </span>&#123;</span><br><span class="line">  <span class="keyword">constructor</span>(x, y) &#123;</span><br><span class="line">    <span class="built_in">Object</span>.assign(<span class="keyword">this</span>, &#123;x, y&#125;);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="（2）为对象添加方法"><a href="#（2）为对象添加方法" class="headerlink" title="（2）为对象添加方法"></a>（2）为对象添加方法</h4><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">Object</span>.assign(SomeClass.prototype, &#123;</span><br><span class="line">  someMethod(arg1, arg2) &#123;</span><br><span class="line">    ···</span><br><span class="line">  &#125;,</span><br><span class="line">  anotherMethod() &#123;</span><br><span class="line">    ···</span><br><span class="line">  &#125;</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 等同于下面的写法</span></span><br><span class="line">SomeClass.prototype.someMethod = <span class="function"><span class="keyword">function</span> (<span class="params">arg1, arg2</span>) </span>&#123;</span><br><span class="line">  ···</span><br><span class="line">&#125;;</span><br><span class="line">SomeClass.prototype.anotherMethod = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">  ···</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<h4 id="（3）克隆对象"><a href="#（3）克隆对象" class="headerlink" title="（3）克隆对象"></a>（3）克隆对象</h4><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">clone</span>(<span class="params">orign</span>)</span>&#123;</span><br><span class="line">    retrun <span class="built_in">Object</span>.assign(&#123;&#125;,orign);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>上述方法只能克隆原始对象的自身的值，不能克隆它的它的继承值。如果想要保持继承链，可以采用下面的代码。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">clone</span>(<span class="params">origin</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">let</span> originProto = <span class="built_in">Object</span>.getPrototypeOf(origin);</span><br><span class="line">  <span class="keyword">return</span> <span class="built_in">Object</span>.assign(<span class="built_in">Object</span>.create(originProto), origin);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="（4）合并多个对象"><a href="#（4）合并多个对象" class="headerlink" title="（4）合并多个对象"></a>（4）合并多个对象</h4><p>将多个对象合并到某个对象。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">cosnt merge = <span class="function">(<span class="params">target,...sources</span>) =&gt;</span> <span class="built_in">Object</span>.assign(target,...sources);</span><br><span class="line"><span class="string">`</span></span><br></pre></td></tr></table></figure>

<h4 id="（5）为属性指定默认值"><a href="#（5）为属性指定默认值" class="headerlink" title="（5）为属性指定默认值"></a>（5）为属性指定默认值</h4><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> DEFAULTS = &#123;</span><br><span class="line">  logLevel: <span class="number">0</span>,</span><br><span class="line">  outputFormat: <span class="string">'html'</span></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">processContent</span>(<span class="params">options</span>) </span>&#123;</span><br><span class="line">  options = <span class="built_in">Object</span>.assign(&#123;&#125;, DEFAULTS, options);</span><br><span class="line">  <span class="built_in">console</span>.log(options);</span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="Object-getOwnPropertyDescriptors"><a href="#Object-getOwnPropertyDescriptors" class="headerlink" title="Object.getOwnPropertyDescriptors()"></a>Object.getOwnPropertyDescriptors()</h2><p>ES5 的 <code>Object.getOwnPropertyDescriptor()</code> 方法会返回某个对象属性的描述对象（<code>descriptor</code>）。ES2017 引入了 <code>Object.getOwnPropertyDescriptors()</code> 方法，返回指定对象所有自身属性（非继承属性）的描述对象。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> obj = &#123;</span><br><span class="line">    foo:<span class="number">123</span>,</span><br><span class="line">    <span class="keyword">get</span> bar() &#123; <span class="keyword">return</span> <span class="string">'abc'</span> &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">Object</span>.getOwnPropertyDescriptors(obj)</span><br><span class="line"><span class="comment">// &#123; foo:</span></span><br><span class="line"><span class="comment">//    &#123; value: 123,</span></span><br><span class="line"><span class="comment">//      writable: true,</span></span><br><span class="line"><span class="comment">//      enumerable: true,</span></span><br><span class="line"><span class="comment">//      configurable: true &#125;,</span></span><br><span class="line"><span class="comment">//   bar:</span></span><br><span class="line"><span class="comment">//    &#123; get: [Function: get bar],</span></span><br><span class="line"><span class="comment">//      set: undefined,</span></span><br><span class="line"><span class="comment">//      enumerable: true,</span></span><br><span class="line"><span class="comment">//      configurable: true &#125; &#125;</span></span><br></pre></td></tr></table></figure>

<h2 id="proto-属性"><a href="#proto-属性" class="headerlink" title="__proto__ 属性"></a><code>__proto__</code> 属性</h2><p><code>__proto__</code> 属性（前后各两个下划线），用来读取或设置当前对象的原型对象（<code>prototype</code>）。目前，所有浏览器（包括 IE11）都部署了这个属性。目前，所有浏览器都部署了这个属性。</p>
<p>它本质上是一个内部属性，而不是一个正式的对外的 API，只是浏览器广泛支持，才被加入了 ES6 。标准明确规定，只有浏览器必须部署这个属性，其他运行环境不一定要部署，而且新的代码最好认为这个属性是不存在的。==建议使用 Object.setPrototypeOf()（写操作）、Object.getPrototypeOf()（读操作）、Object.create()（生成操作）代替。==</p>
<h2 id="Object-setPrototypeOf-、Object-getPrototypeOf"><a href="#Object-setPrototypeOf-、Object-getPrototypeOf" class="headerlink" title="Object.setPrototypeOf()、Object.getPrototypeOf()"></a>Object.setPrototypeOf()、Object.getPrototypeOf()</h2><p><code>Object.setPrototypeOf()</code> 方法的作用与 <code>__proto__</code> 相同，用来设置一个对象的 <code>prototype</code> 对象，返回参数本身。它是 ES6 正式推荐的设置原型对象的方法。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 格式</span></span><br><span class="line"><span class="built_in">Object</span>.setPrototypeOf(object,prototype)</span><br><span class="line"></span><br><span class="line"><span class="comment">//用法</span></span><br><span class="line"><span class="keyword">const</span> o = <span class="built_in">Object</span>.setPrototypeOf(&#123;&#125;,<span class="literal">null</span>);</span><br></pre></td></tr></table></figure>

<p>如果第一个参数不是对象，会自动转为对象。由于 <code>undefined</code> 和 <code>null</code> 无法转为对象，所以如果第一个参数是 <code>undefined</code> 或 <code>null</code>，就会报错。</p>
<p><code>Object.getPrototypeOf()</code> 与 <code>Object.setProrotypeOf()</code>  方法配套，用于读取一个对象的原型对象。如果参数不是对象，会被自动转为对象。如果参数是 <code>undefined</code> 或 <code>null</code>，它们无法转为对象，所以会报错。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">Object</span>.getPrototypeOf(obj);</span><br></pre></td></tr></table></figure>

<h2 id="Object-keys-、Object-values-、Object-entries"><a href="#Object-keys-、Object-values-、Object-entries" class="headerlink" title="Object.keys()、Object.values()、Object.entries()"></a>Object.keys()、Object.values()、Object.entries()</h2><p>ES5 引入了 <code>Object.keys</code> 方法，返回一个数组，成员是参数对象自身（不含继承的）所有可遍历属性的键名。</p>
<p>ES2017 引入了跟 <code>Object.keys</code> 配套的 <code>Object.values</code> 和 <code>Object.entries</code>，作为遍历一个对象的补充手段，供 <code>for ... of</code> 循环使用。</p>
<p><code>Object.values</code> 方法返回一个数组，成员是参数对象自身的（不含继承的）所有可遍历属性的键值。</p>
<p><code>Object.entries</code> 方法返回一个数组，成员是参数对象自身的（不含继承的）所有可遍历属性的键值对数组。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> &#123;keys,values,entries&#125; = object;</span><br><span class="line"><span class="keyword">let</span> obj = &#123;<span class="attr">a</span>:<span class="number">1</span>,<span class="attr">b</span>:<span class="number">2</span>,<span class="attr">c</span>:<span class="number">3</span>&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">let</span> key <span class="keyword">of</span> keys(obj))&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(key); <span class="comment">// 'a','b','c'</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">let</span> value <span class="keyword">of</span> values(obj))&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(value); <span class="comment">// 1,2,3</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">let</span> [key,value] <span class="keyword">of</span> entries(obj))&#123;</span><br><span class="line">    <span class="built_in">console</span>.log([key,value]); <span class="comment">// ['a', 1], ['b', 2], ['c', 3]&#125;</span></span><br></pre></td></tr></table></figure>

<h1 id="Symbol"><a href="#Symbol" class="headerlink" title="Symbol"></a>Symbol</h1><h2 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h2><p>ES6 引入了一种新的原始数据类型 Symbol ，==表示独一无二的值。==</p>
<p>==它是 JavaScript 语言的第七种数据类型，前六种是：undefined、null、布尔值、字符串、数值、对象。==</p>
<p>Symbol 值通过 Symbol 函数生成。对象的属性名现在可以有两种类型，==一种是原来就有的字符串，另一种就是 Symbol 类型。== 凡是属性名属于 Symbol 类型，就都是独一无二的，可以保证不会与其他属性名产生冲突。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> s = <span class="built_in">Symbol</span>();</span><br><span class="line"><span class="keyword">typeof</span> s; <span class="comment">// "symbol"</span></span><br></pre></td></tr></table></figure>

<p>Symbol 函数前不能使用 <code>new</code> 命令，否则会报错。这是因为生成的 Symbol 是一个原始类型的值，不是对象。也就是说，由于 Symbol 值不是对象，所以不能添加属性。==基本上，它是一种类似于字符串的数据类型。==</p>
<p>Symbol 函数可以接受一个字符串作为参数，表示对 Symbol 实例的描述，主要是为了控制台显示，或者转为字符串时，比较容易区分。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> s1 = <span class="built_in">Symbol</span>(<span class="string">'foo'</span>);</span><br><span class="line"><span class="keyword">let</span> s2 = <span class="built_in">Symbol</span>(<span class="string">'bar'</span>);</span><br><span class="line"></span><br><span class="line">s1 <span class="comment">// Symbol(foo)</span></span><br><span class="line">s2 <span class="comment">// Symbol(bar)</span></span><br><span class="line"></span><br><span class="line">s1.toString() <span class="comment">// "Symbol(foo)"</span></span><br><span class="line">s2.toString() <span class="comment">// "Symbol(bar)"</span></span><br></pre></td></tr></table></figure>

<p>Symbol 函数的参数只是表示对当前 Symbol 值的描述，因此==相同参数的 Symbol 函数的返回值是不相等的。==</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 没有参数的情况</span></span><br><span class="line"><span class="keyword">let</span> s1 = <span class="built_in">Symbol</span>();</span><br><span class="line"><span class="keyword">let</span> s2 = <span class="built_in">Symbol</span>();</span><br><span class="line"></span><br><span class="line">s1 === s2 <span class="comment">// false</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 有参数的情况</span></span><br><span class="line"><span class="keyword">let</span> s1 = <span class="built_in">Symbol</span>(<span class="string">'foo'</span>);</span><br><span class="line"><span class="keyword">let</span> s2 = <span class="built_in">Symbol</span>(<span class="string">'foo'</span>);</span><br><span class="line"></span><br><span class="line">s1 === s2 <span class="comment">// false</span></span><br></pre></td></tr></table></figure>

<p>Symbol 值不能与其他类型的值进行运算，会报错。但是，Symbol 值可以显式转为字符串。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">"hello"</span> + <span class="built_in">Symbol</span>(<span class="string">'abc'</span>); <span class="comment">// 报错</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> sym = <span class="built_in">Symbol</span>(<span class="string">'My symbol'</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">String</span>(sym) <span class="comment">// 'Symbol(My symbol)'</span></span><br><span class="line">sym.toString() <span class="comment">// 'Symbol(My symbol)'</span></span><br></pre></td></tr></table></figure>

<h2 id="作为属性名的-Symbol"><a href="#作为属性名的-Symbol" class="headerlink" title="作为属性名的 Symbol"></a>作为属性名的 Symbol</h2><p>Symbol 用于对象的属性名，可以保证不会出现同名的属性。这对于一个对象由多个模块构成的情况很有用，防止某个键不小心改写或者覆盖。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> mySymbol = <span class="built_in">Symbol</span>();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 第一种写法</span></span><br><span class="line"><span class="keyword">let</span> a = &#123;&#125;;</span><br><span class="line">a[mySymbol] = <span class="string">'Hello!'</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 第二种写法</span></span><br><span class="line"><span class="keyword">let</span> a = &#123;</span><br><span class="line">  [mySymbol]: <span class="string">'Hello!'</span></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 第三种写法</span></span><br><span class="line"><span class="keyword">let</span> a = &#123;&#125;;</span><br><span class="line"><span class="built_in">Object</span>.defineProperty(a, mySymbol, &#123; <span class="attr">value</span>: <span class="string">'Hello!'</span> &#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 以上写法都得到同样结果</span></span><br><span class="line">a[mySymbol] <span class="comment">// "Hello!"</span></span><br></pre></td></tr></table></figure>

<p>Symbol 值作为对象属性名时，==不能用点运算符==，因为点运算符后面总是字符串，会导致点运算符后面的属性名取值是个字符串，而不是 Symbol 值。同理，在对象内部，==使用 Symbol 定义属性时，Symbol 值必须放在方括号中。==</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">cosnt mySymbol = <span class="built_in">Symbol</span>();</span><br><span class="line">cosnt a = &#123;&#125;;</span><br><span class="line">a.mySymbol = <span class="string">'Hello'</span>;</span><br><span class="line">a[mySymbol] <span class="comment">// undefined</span></span><br><span class="line">a[<span class="string">'mySymbol'</span>] <span class="comment">// 'Hello'</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> s = <span class="built_in">Symbol</span>();</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> obj = &#123;</span><br><span class="line">    [s](arg)&#123; ... &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>==Symbol 值作为属性名时，该属性还是公开属性，不是私有属性。==</p>
<h2 id="属性名的遍历"><a href="#属性名的遍历" class="headerlink" title="属性名的遍历"></a>属性名的遍历</h2><p>Symbol 作为属性名，遍历对象的时候，该属性不会出现在 <code>for ... in 、for ... of 循环</code>中，也不会被 <code>Object.keys()、Object.getOwnPropertyNames()、JSON.stringify()</code> 返回。</p>
<p>但是它不是私有属性。使用 <code>Object.geOwnPropertySymbols()</code> 方法，可以获取指定对象的所有 Symbol 属性名。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> obj = &#123;&#125;;</span><br><span class="line"><span class="keyword">let</span> a = <span class="built_in">Symbol</span>(<span class="string">'a'</span>);</span><br><span class="line"><span class="keyword">let</span> b = <span class="built_in">Symbol</span>(<span class="string">'b'</span>);</span><br><span class="line"></span><br><span class="line">obj[a] = <span class="string">'Hello'</span>;</span><br><span class="line">obj[b] = <span class="string">'World'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> objectSymbols = <span class="built_in">Object</span>.getOwnPropertySymbols(obj);</span><br><span class="line"></span><br><span class="line">objectSymbols <span class="comment">// [Symbol(a),Symbol(b)]</span></span><br></pre></td></tr></table></figure>

<p>另一个新的 API，<code>Reflect.ownKeys()</code> 方法可以返回所有类型的键名，包括常规键名和 Symbol 键名。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> obj = &#123;</span><br><span class="line">    [<span class="built_in">Symbol</span>(<span class="string">'my_key'</span>)]:<span class="number">1</span>,</span><br><span class="line">    enum:<span class="number">2</span>,</span><br><span class="line">    nonEnum:<span class="number">3</span></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="built_in">Reflect</span>.ownKeys(obj)</span><br><span class="line"><span class="comment">// ["enum", "nonEnum", Symbol(my_key)]</span></span><br></pre></td></tr></table></figure>

<p>由于以 Symbol 值作为键名，不会被常规方法遍历得到。所以可以根据这一特性，定义一些非私有的，但又希望只用于内部的方法。</p>
<h2 id="Symbol-for-、Symbol-keyFor"><a href="#Symbol-for-、Symbol-keyFor" class="headerlink" title="Symbol.for()、Symbol.keyFor()"></a>Symbol.for()、Symbol.keyFor()</h2><p>==<code>Symbol.for()</code> 方法接受一个字符串作为参数，然后搜索有没有以该参数作为名称的 Symbol 值。如果有，就返回这个 Symbol 值，否则就新建一个以该字符串为名称的 Symbol 值，并将其注册到全局。==</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> s1 = <span class="built_in">Symbol</span>.for(<span class="string">'foo'</span>);</span><br><span class="line"><span class="keyword">let</span> s2 = <span class="built_in">Symbol</span>.for(<span class="string">'foo'</span>);</span><br><span class="line"></span><br><span class="line">s1 === s2 <span class="comment">// true</span></span><br></pre></td></tr></table></figure>

<p><code>Symbol.for()</code> 与 <code>Symbol()</code> 这两种写法，都会生成新的 Symbol。它们的区别是，==前者会被登记在全局环境中搜索，后者不会。<code>Symbol.for()</code> 不会每次调用就返回一个新的 Symbol 类型的值，而是会先检查给定的 key 是否已经存在，如果不存在才会新建一个值。==</p>
<p><code>Symbol.keyFor()</code> 方法返回一个已经登记的 Symbol 类型值的 key。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> s1 = <span class="built_in">Symbol</span>.for(<span class="string">'foo'</span>);</span><br><span class="line"><span class="built_in">Symbol</span>.keyFor(s1) <span class="comment">// 'foo'</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> s2 = <span class="built_in">Symbol</span>(<span class="string">'foo'</span>);</span><br><span class="line"><span class="built_in">Symbol</span>.keyFor(s2) <span class="comment">// undefined</span></span><br></pre></td></tr></table></figure>

<p>上面代码中，变量 <code>s2</code> 属于未登记的 Symbol 值，所以返回  <code>undefined</code>。</p>
<p><code>Symbol.for()</code> 为 Symbol 值登记的名字，是全局环境的，不管有没有在全局环境中运行。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">Symbol</span>.for(<span class="string">'bar'</span>)</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> x = foo();</span><br><span class="line">cosnt y = <span class="built_in">Symbol</span>.for(<span class="string">'bar'</span>);</span><br><span class="line"><span class="built_in">console</span>.log(x === y) <span class="comment">// true</span></span><br></pre></td></tr></table></figure>

<h1 id="Set、WeakSet"><a href="#Set、WeakSet" class="headerlink" title="Set、WeakSet"></a>Set、WeakSet</h1><h2 id="set-简介"><a href="#set-简介" class="headerlink" title="set 简介"></a>set 简介</h2><p>ES6 提供了新的数据结构 Set。它类似于数组，但是成员的值都是唯一的，没有重复的值。</p>
<p>Set 本身是一个构造函数，用来生成 Set 数据结构。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> s = <span class="keyword">new</span> <span class="built_in">Set</span>();</span><br><span class="line"></span><br><span class="line">[<span class="number">2</span>, <span class="number">3</span>, <span class="number">5</span>, <span class="number">4</span>, <span class="number">5</span>, <span class="number">2</span>, <span class="number">2</span>].forEach(<span class="function"><span class="params">x</span> =&gt;</span> s.add(x));</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">let</span> i <span class="keyword">of</span> s)&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(i);</span><br><span class="line">&#125; <span class="comment">// 2 3 5 4</span></span><br></pre></td></tr></table></figure>

<p>Set 函数可以接受一个数组（或者具有 <code>iterable</code> 接口的其他数据结构）作为参数，用来初始化。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 例一</span></span><br><span class="line"><span class="keyword">const</span> <span class="keyword">set</span> = new Set([1,2,3,4,4]);</span><br><span class="line">[...<span class="keyword">set</span>] // [1,2,3,4]</span><br><span class="line"></span><br><span class="line">// 例二</span><br><span class="line">const items = new Set([1, 2, 3, 4, 5, 5, 5, 5]);</span><br><span class="line">items.size // 5</span><br><span class="line"></span><br><span class="line">// 例三</span><br><span class="line">const <span class="keyword">set</span> = new Set(document.querySelectorAll('div'))</span><br><span class="line"><span class="keyword">set</span>.size // 56</span><br></pre></td></tr></table></figure>

<p>利用 Set 进行数组去重：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[...new <span class="built_in">Set</span>(array)]</span><br></pre></td></tr></table></figure>

<p>利用 Set 进行字符串里面的字符去重：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[...new <span class="built_in">Set</span>(<span class="string">'ababbc'</span>)].join(<span class="string">''</span>) <span class="comment">// 'abc'</span></span><br></pre></td></tr></table></figure>

<p>==向 Set 加入值的时候，不会发生类型转换==，所以 5 和 ‘5’ 是两个不同的值。</p>
<h2 id="Set-实例的属性和方法"><a href="#Set-实例的属性和方法" class="headerlink" title="Set 实例的属性和方法"></a>Set 实例的属性和方法</h2><p>Set 结构的实例有以下属性：</p>
<ul>
<li><code>Set.prototype.constructor</code>：构造函数，默认就是 Set 函数</li>
<li><code>Set.prototype.size</code>：返回 Set 实例的成员总数</li>
</ul>
<p>Set 实例的操作方法（用于操作数据）：</p>
<ul>
<li><code>Set.prototype.add(value)</code>：添加某个值，返回 Set 结构本身</li>
<li><code>Set.prototype.delete(value)</code>：删除某个值，返回一个布尔值，表示删除是否成功</li>
<li><code>Set.prototype.has(value)</code>：返回一个布尔值，表示该值是否为 Set 的成员</li>
<li><code>Set.prototype.clear()</code>：清除所有成员，没有返回值</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">s.add(<span class="number">1</span>).add(<span class="number">2</span>).add(<span class="number">2</span>); <span class="comment">// 注意 2 被加入了两次</span></span><br><span class="line"></span><br><span class="line">s.size <span class="comment">// 2</span></span><br><span class="line"></span><br><span class="line">s.has(<span class="number">1</span>) <span class="comment">// ture</span></span><br><span class="line">s.has(<span class="number">2</span>) <span class="comment">// true</span></span><br><span class="line">s.has(<span class="number">3</span>) <span class="comment">// false</span></span><br><span class="line"></span><br><span class="line">s.delete(<span class="number">2</span>);</span><br><span class="line">s.has(<span class="number">2</span>) <span class="comment">// false</span></span><br></pre></td></tr></table></figure>

<p><code>Array.from</code> 方法可将 Set 结构转为数组。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> items = <span class="keyword">new</span> <span class="built_in">Set</span>([<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>,<span class="number">4</span>,<span class="number">5</span>]);</span><br><span class="line"><span class="keyword">const</span> array = <span class="built_in">Array</span>.from(items);</span><br></pre></td></tr></table></figure>

<p>数组去重：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">dedupe</span>(<span class="params">array</span>)</span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">Array</span>.from(<span class="keyword">new</span> <span class="built_in">Set</span>(array));</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">dedupe([<span class="number">1</span>,<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>]) <span class="comment">// [1,2,3]</span></span><br></pre></td></tr></table></figure>

<h2 id="Set-结构遍历"><a href="#Set-结构遍历" class="headerlink" title="Set 结构遍历"></a>Set 结构遍历</h2><p>Set 结构的实例有 4 个遍历方法：</p>
<ul>
<li><code>Set.prototype.keys()</code>：返回键名的遍历器</li>
<li><code>Set.prototype.values()</code>：返回键值的遍历器</li>
<li><code>Set.prototype.entres()</code>：返回键值对的遍历器</li>
<li><code>Set.prototype.forEach()</code>：使用回调函数遍历每个成员</li>
</ul>
<p>==Set 的遍历顺序就是插入顺序。== 这个特性有时候很有用，比如使用 Set 保存一个回调函数列表，调用时就能保证按照添加顺序调用。</p>
<h3 id="keys-，values-，entries"><a href="#keys-，values-，entries" class="headerlink" title="keys()，values()，entries()"></a>keys()，values()，entries()</h3><p>由于 Set 结构没有键名，只有键值（或者说键名和键值是同一个值），所以 <code>keys</code> 方法和 <code>values</code> 方法的行为完全一致。遍历时，也==可省略 <code>vlaues</code> 方法，直接用 <code>for ... of</code> 循环遍历 Set 。==</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> <span class="keyword">set</span> = new Set(['red','green','blue']);</span><br><span class="line"></span><br><span class="line">for(let item of <span class="keyword">set</span>.keys())&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(item);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// red</span></span><br><span class="line"><span class="comment">// green</span></span><br><span class="line"><span class="comment">// blue</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">let</span> item <span class="keyword">of</span> <span class="keyword">set</span>.values())&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(item);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// red</span></span><br><span class="line"><span class="comment">// green</span></span><br><span class="line"><span class="comment">// blue</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">let</span> item <span class="keyword">of</span> <span class="keyword">set</span>.entries())&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(item);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// ['red','red']</span></span><br><span class="line"><span class="comment">// ['green','green']</span></span><br><span class="line"><span class="comment">// ['blue','blue']</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 省略values用法</span></span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">let</span> x <span class="keyword">of</span> <span class="keyword">set</span>)&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(x);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// red</span></span><br><span class="line"><span class="comment">// green</span></span><br><span class="line"><span class="comment">// blue</span></span><br></pre></td></tr></table></figure>

<h3 id="forEach"><a href="#forEach" class="headerlink" title="forEach()"></a>forEach()</h3><p>Set 结构的实例与数组一样，也拥有 <code>forEach</code> 方法，用于对每个成员执行某种操作，没有返回值。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> <span class="keyword">set</span> = new Set([1, 4, 9]);</span><br><span class="line"><span class="keyword">set</span>.forEach((value, key) =&gt; console.log(key + ' : ' + value))</span><br><span class="line">// 1 : 1</span><br><span class="line">// 4 : 4</span><br><span class="line">// 9 : 9</span><br></pre></td></tr></table></figure>

<p>==Set 结构的键名就是键值（两者是同一个值）==，因此第一个参数与第二个参数的值永远都是一样的。</p>
<h3 id="遍历的应用"><a href="#遍历的应用" class="headerlink" title="遍历的应用"></a>遍历的应用</h3><p>扩展运算符（<code>...</code>）内部使用 <code>for...of</code> 循环，所以也可以用于 Set 结构。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> <span class="keyword">set</span> = new Set(['red', 'green', 'blue']);</span><br><span class="line">let arr = [...<span class="keyword">set</span>];</span><br><span class="line">// ['red', 'green', 'blue']</span><br></pre></td></tr></table></figure>

<p>扩展运算符和 Set 结构相结合，就可以去除数组的重复成员。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> arr = [<span class="number">3</span>, <span class="number">5</span>, <span class="number">2</span>, <span class="number">2</span>, <span class="number">5</span>, <span class="number">5</span>];</span><br><span class="line"><span class="keyword">let</span> unique = [...new <span class="built_in">Set</span>(arr)];</span><br><span class="line"><span class="comment">// [3, 5, 2]</span></span><br></pre></td></tr></table></figure>

<p>而且，数组的 <code>map</code> 和 <code>filter</code> 方法也可以间接用于 Set 了。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> <span class="keyword">set</span> = new Set([1, 2, 3]);</span><br><span class="line"><span class="keyword">set</span> = new Set([...<span class="keyword">set</span>].map(x =&gt; x * 2));</span><br><span class="line">// 返回Set结构：&#123;<span class="number">2</span>, <span class="number">4</span>, <span class="number">6</span>&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> <span class="keyword">set</span> = new Set([1, 2, 3, 4, 5]);</span><br><span class="line"><span class="keyword">set</span> = new Set([...<span class="keyword">set</span>].filter(x =&gt; (x % 2) == 0));</span><br><span class="line">// 返回Set结构：&#123;<span class="number">2</span>, <span class="number">4</span>&#125;</span><br></pre></td></tr></table></figure>


<h2 id="WeakSet"><a href="#WeakSet" class="headerlink" title="WeakSet"></a>WeakSet</h2><h3 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h3><p>WeakSet 结构与 Set 类似，也是不重复的值的集合。但是，它与 Set 有两个区别。</p>
<ol>
<li>WeakSet 的成员只能是对象，而不能是其他类型的值。</li>
</ol>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> ws = <span class="keyword">new</span> <span class="built_in">WeakSet</span>();</span><br><span class="line">ws.add(<span class="number">1</span>)</span><br><span class="line"><span class="comment">// TypeError: Invalid value used in weak set</span></span><br><span class="line">ws.add(<span class="built_in">Symbol</span>())</span><br><span class="line"><span class="comment">// TypeError: invalid value used in weak set</span></span><br></pre></td></tr></table></figure>

<ol start="2">
<li>WeakSet 中的对象都是弱引用，即垃圾回收机制不考虑 WeakSet 对该对象的引用，也就是说，如果其他对象都不再引用该对象，那么垃圾回收机制会自动回收该对象所占用的内存，不考虑该对象还存在于 WeakSet 之中。</li>
</ol>
<h3 id="语法"><a href="#语法" class="headerlink" title="语法"></a>语法</h3><p>WeakSet 是一个构造函数，可以使用 new 命令，创造 WeakSet 数据结构。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> ws = <span class="keyword">new</span> <span class="built_in">WeakSet</span>();</span><br></pre></td></tr></table></figure>

<p>WeakSet 结构有三个方法：</p>
<ul>
<li><code>WeakSet.prorotype.add(value)</code>：向 WeakSet 实例添加一个新成员；</li>
<li><code>WeakSet.prototype.delete(value)</code>：清除 WeakSet 实例的指定成员；</li>
<li><code>WeakSet.prorotype.has(value)</code>：返回一个布尔值，表示某个值是否在 WeakSet 实例中。</li>
</ul>
<p>WeakSet 不能遍历，是因为成员都是弱引用，随时可能消失，遍历机制无法保证成员的存在，很可能刚刚遍历结束，成员就取不到了。==WeakSet 的一个用处，是储存 DOM 节点，而不用担心这些节点从文档移除时，会引发内存泄漏。==</p>
<h1 id="Map、WeakMap"><a href="#Map、WeakMap" class="headerlink" title="Map、WeakMap"></a>Map、WeakMap</h1><h2 id="Map-简介"><a href="#Map-简介" class="headerlink" title="Map 简介"></a>Map 简介</h2><p>JavaScript 的对象，本质上是键值对的集合（Hash结构），但是传统上只能用字符串当作键。这给它的使用带了了一些局限性，比如将一个 DOM 节点作为对象 <code>data</code> 的键，但是由于对象只接受字符串作为键名，所以 <code>element</code> 被自动转为字符串 <code>[object HTMLDivElement]</code> 。</p>
<p>ES6 提供了 Map 数据结构。它类似于对象，也是键值对的集合，但是“键”的范围不限于字符串，各种类型的值（包括对象）都可以当作键。Object 结构提供了“字符串—值”的对应，Map 结构提供了“值—值”的对应，是一种更完善的 Hash 结构实现。如果你需要“键值对”的数据结构，Map 比 Object 更合适。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> m = <span class="keyword">new</span> <span class="built_in">Map</span>();</span><br><span class="line"><span class="keyword">const</span> o = &#123;<span class="attr">p</span>:<span class="string">'Hello World'</span>&#125;;</span><br><span class="line"></span><br><span class="line">m.set(o,<span class="string">'content'</span>)</span><br><span class="line">m.get(o) <span class="comment">// "content"</span></span><br><span class="line"></span><br><span class="line">m.has(o) <span class="comment">// true</span></span><br><span class="line">m.delete(o) <span class="comment">// true</span></span><br><span class="line">m.has(o) <span class="comment">// false</span></span><br></pre></td></tr></table></figure>

<p>Map 也可以接受一个数组作为参数：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> map = <span class="keyword">new</span> <span class="built_in">Map</span>([</span><br><span class="line">    [<span class="string">'name'</span>,<span class="string">'张三'</span>],</span><br><span class="line">    [<span class="string">'title'</span>,<span class="string">'Author'</span>]</span><br><span class="line">])</span><br><span class="line"></span><br><span class="line">map.size <span class="comment">// 2</span></span><br><span class="line">map.has(<span class="string">'name'</span>) <span class="comment">// true</span></span><br><span class="line">map.get(<span class="string">'name'</span>) <span class="comment">// "张三"</span></span><br><span class="line">map.has(<span class="string">'title'</span>) <span class="comment">// true</span></span><br><span class="line">map.get(<span class="string">'title'</span>) <span class="comment">// "Author"</span></span><br></pre></td></tr></table></figure>

<p>任何具有 <code>Iterator</code> 接口、且每个成员都是一个双元素的数组的数据结构都可以当作 Map 构造函数的参数。这就是说，Set 和 Map都可以用来生成新的 Map。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> <span class="keyword">set</span> = new Set([</span><br><span class="line">  ['foo', 1],</span><br><span class="line">  ['bar', 2]</span><br><span class="line">]);</span><br><span class="line">const m1 = new Map(<span class="keyword">set</span>);</span><br><span class="line">m1.<span class="keyword">get</span>('foo') // 1</span><br><span class="line"></span><br><span class="line">const m2 = new Map([['baz', 3]]);</span><br><span class="line">const m3 = new Map(m2);</span><br><span class="line">m3.<span class="keyword">get</span>('baz') // 3</span><br></pre></td></tr></table></figure>

<p>如果对同一个键多次赋值，后面的值将覆盖前面的值。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> map = <span class="keyword">new</span> <span class="built_in">Map</span>();</span><br><span class="line"></span><br><span class="line">map</span><br><span class="line">.set(<span class="number">1</span>, <span class="string">'aaa'</span>)</span><br><span class="line">.set(<span class="number">1</span>, <span class="string">'bbb'</span>);</span><br><span class="line"></span><br><span class="line">map.get(<span class="number">1</span>) <span class="comment">// "bbb"</span></span><br></pre></td></tr></table></figure>

<p>注意：==只有对同一个对象的引用，Map 接口才将其视为同一个键。==</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> map = <span class="keyword">new</span> <span class="built_in">Map</span>();</span><br><span class="line"></span><br><span class="line">map.set([<span class="string">'a'</span>],<span class="number">555</span>);</span><br><span class="line">map.get([<span class="string">'a'</span>]) <span class="comment">// undefined</span></span><br></pre></td></tr></table></figure>

<p>上面的 <code>set</code> 和 <code>get</code> 方法，表面上针对同一个键，但实际上这两个不同的数组实例，内存地址不一样，因此 <code>get</code> 方法无法读取该键，返回 <code>undefined</code>。</p>
<p>同理，==同样的值的两个实例，在 Map 结构中被视为两个键。==</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> map = <span class="keyword">new</span> <span class="built_in">Map</span>();</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> k1 = [<span class="string">'a'</span>];</span><br><span class="line"><span class="keyword">const</span> k2 = [<span class="string">'a'</span>];</span><br><span class="line"></span><br><span class="line">map.set(k1,<span class="number">111</span>).set(k2,<span class="number">222</span>);</span><br><span class="line"></span><br><span class="line">map.get(k1) <span class="comment">// 111</span></span><br><span class="line">map.get(k2) <span class="comment">// 222</span></span><br></pre></td></tr></table></figure>

<p>上述案例，变量的 <code>k1</code> 和 <code>k2</code> 的值是一样的，但是它们在 Map 结构中被视为两个键。</p>
<p>Map 的键实际上跟内存地址绑定的，只要内存地址不一样，就视为两个键。这就解决了同名属性碰撞（clash）的问题，我们扩展别人的库的时候，如果使用对象作为键名，就不用担心自己的属性与原作者的属性同名。</p>
<p>如果 Map 的键是一个简单类型的值（数字、字符串、布尔值），则只要两个值严格相等，Map 将其视为一个键，比如 <code>0</code> 和 <code>-0</code> 就是一个键，布尔值 <code>true</code> 和 <code>字符串true</code> 则是两个不同的键。另外，<code>undefined</code> 和 <code>null</code> 也是两个不同的键。==虽然 <code>NaN</code> 不严格相等于自身，但 Map 将其视为同一个键。==</p>
<h2 id="Map-实例属性和操作方法"><a href="#Map-实例属性和操作方法" class="headerlink" title="Map 实例属性和操作方法"></a>Map 实例属性和操作方法</h2><ul>
<li><code>size</code> 属性：<code>size</code> 属性返回 Map 结构的成员总数</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> map = <span class="keyword">new</span> <span class="built_in">Map</span>();</span><br><span class="line">map.set(<span class="string">'foo'</span>, <span class="literal">true</span>);</span><br><span class="line">map.set(<span class="string">'bar'</span>, <span class="literal">false</span>);</span><br><span class="line"></span><br><span class="line">map.size <span class="comment">// 2</span></span><br></pre></td></tr></table></figure>

<ul>
<li><code>Map.prototype.set(key,value)</code>：<code>set</code> 方法设置键名 <code>key</code> 对应的键值为 <code>value</code>，然后返回整个 Map 结构。如果 <code>key</code> 已经有值，则键值会被更新，否则就新生成该键。</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> m = <span class="keyword">new</span> <span class="built_in">Map</span>();</span><br><span class="line"></span><br><span class="line">m.set(<span class="string">'edition'</span>, <span class="number">6</span>)        <span class="comment">// 键是字符串</span></span><br><span class="line">m.set(<span class="number">262</span>, <span class="string">'standard'</span>)     <span class="comment">// 键是数值</span></span><br><span class="line">m.set(<span class="literal">undefined</span>, <span class="string">'nah'</span>)    <span class="comment">// 键是 undefined</span></span><br></pre></td></tr></table></figure>

<p><code>set</code> 方法返回的是当前的 Map 对象，因此可以采用链式写法。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> map = <span class="keyword">new</span> <span class="built_in">Map</span>()</span><br><span class="line">  .set(<span class="number">1</span>, <span class="string">'a'</span>)</span><br><span class="line">  .set(<span class="number">2</span>, <span class="string">'b'</span>)</span><br><span class="line">  .set(<span class="number">3</span>, <span class="string">'c'</span>);</span><br></pre></td></tr></table></figure>

<ul>
<li><code>Map.prototype.get(key)</code>：<code>get</code>  方法读取 <code>key</code> 对应的键值，如果找不到 <code>key</code>，返回 <code>undefined</code>。</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> m = <span class="keyword">new</span> <span class="built_in">Map</span>();</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> hello = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;<span class="built_in">console</span>.log(<span class="string">'hello'</span>);&#125;;</span><br><span class="line">m.set(hello, <span class="string">'Hello ES6!'</span>) <span class="comment">// 键是函数</span></span><br><span class="line"></span><br><span class="line">m.get(hello)  <span class="comment">// Hello ES6!</span></span><br></pre></td></tr></table></figure>

<ul>
<li><code>Map.prototype.has(key)</code>：<code>has</code> 方法返回一个布尔值，表示某个键是否在当前 Map 对象之中。</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> m = <span class="keyword">new</span> <span class="built_in">Map</span>();</span><br><span class="line"></span><br><span class="line">m.set(<span class="string">'edition'</span>, <span class="number">6</span>);</span><br><span class="line">m.set(<span class="number">262</span>, <span class="string">'standard'</span>);</span><br><span class="line">m.set(<span class="literal">undefined</span>, <span class="string">'nah'</span>);</span><br><span class="line"></span><br><span class="line">m.has(<span class="string">'edition'</span>)     <span class="comment">// true</span></span><br><span class="line">m.has(<span class="string">'years'</span>)       <span class="comment">// false</span></span><br><span class="line">m.has(<span class="number">262</span>)           <span class="comment">// true</span></span><br><span class="line">m.has(<span class="literal">undefined</span>)     <span class="comment">// true</span></span><br></pre></td></tr></table></figure>

<ul>
<li><code>Map.prototype.delete(key)</code>：<code>delete</code> 方法删除某个键，返回 <code>true</code>。如果删除失败，返回 <code>false</code>。</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> m = <span class="keyword">new</span> <span class="built_in">Map</span>();</span><br><span class="line">m.set(<span class="literal">undefined</span>, <span class="string">'nah'</span>);</span><br><span class="line">m.has(<span class="literal">undefined</span>)     <span class="comment">// true</span></span><br><span class="line"></span><br><span class="line">m.delete(<span class="literal">undefined</span>)</span><br><span class="line">m.has(<span class="literal">undefined</span>)       <span class="comment">// false</span></span><br></pre></td></tr></table></figure>

<ul>
<li><code>Map.prototype.clear()</code>：<code>clear</code> 方法清除所有成员，没有返回值。</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> map = <span class="keyword">new</span> <span class="built_in">Map</span>();</span><br><span class="line">map.set(<span class="string">'foo'</span>, <span class="literal">true</span>);</span><br><span class="line">map.set(<span class="string">'bar'</span>, <span class="literal">false</span>);</span><br><span class="line"></span><br><span class="line">map.size <span class="comment">// 2</span></span><br><span class="line">map.clear()</span><br><span class="line">map.size <span class="comment">// 0</span></span><br></pre></td></tr></table></figure>

<h2 id="Map-结构遍历"><a href="#Map-结构遍历" class="headerlink" title="Map 结构遍历"></a>Map 结构遍历</h2><p>Map 结构原生提供三个遍历器生成函数和一个遍历方法：</p>
<ul>
<li><code>Map.prototype.keys()</code>：返回键名的遍历器</li>
<li><code>Map.prototype.values()</code>：返回键值的遍历器</li>
<li><code>Map.prototype.entries()</code>：返回所有成员的遍历器</li>
<li><code>Map.prototype.forEach()</code>：遍历 Map 的所有成员</li>
</ul>
<p>==Map 的遍历顺序就是插入顺序。==</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> map = <span class="keyword">new</span> <span class="built_in">Map</span>([</span><br><span class="line">  [<span class="string">'F'</span>, <span class="string">'no'</span>],</span><br><span class="line">  [<span class="string">'T'</span>,  <span class="string">'yes'</span>],</span><br><span class="line">]);</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">let</span> key <span class="keyword">of</span> map.keys()) &#123;</span><br><span class="line">  <span class="built_in">console</span>.log(key);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// "F"</span></span><br><span class="line"><span class="comment">// "T"</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">let</span> value <span class="keyword">of</span> map.values()) &#123;</span><br><span class="line">  <span class="built_in">console</span>.log(value);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// "no"</span></span><br><span class="line"><span class="comment">// "yes"</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">let</span> item <span class="keyword">of</span> map.entries()) &#123;</span><br><span class="line">  <span class="built_in">console</span>.log(item[<span class="number">0</span>], item[<span class="number">1</span>]);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// "F" "no"</span></span><br><span class="line"><span class="comment">// "T" "yes"</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 或者</span></span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">let</span> [key, value] <span class="keyword">of</span> map.entries()) &#123;</span><br><span class="line">  <span class="built_in">console</span>.log(key, value);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// "F" "no"</span></span><br><span class="line"><span class="comment">// "T" "yes"</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 等同于使用map.entries()</span></span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">let</span> [key, value] <span class="keyword">of</span> map) &#123;</span><br><span class="line">  <span class="built_in">console</span>.log(key, value);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// "F" "no"</span></span><br><span class="line"><span class="comment">// "T" "yes"</span></span><br></pre></td></tr></table></figure>

<h2 id="与其他数据结构的相互转换"><a href="#与其他数据结构的相互转换" class="headerlink" title="与其他数据结构的相互转换"></a>与其他数据结构的相互转换</h2><h3 id="Map-转为数组"><a href="#Map-转为数组" class="headerlink" title="Map 转为数组"></a>Map 转为数组</h3><p>Map 转为数组最方便的方法，就是使用扩展运算符（<code>...</code>）。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> myMap = <span class="keyword">new</span> <span class="built_in">Map</span>()</span><br><span class="line">  .set(<span class="literal">true</span>, <span class="number">7</span>)</span><br><span class="line">  .set(&#123;<span class="attr">foo</span>: <span class="number">3</span>&#125;, [<span class="string">'abc'</span>]);</span><br><span class="line">[...myMap]</span><br><span class="line"><span class="comment">// [ [ true, 7 ], [ &#123; foo: 3 &#125;, [ 'abc' ] ] ]</span></span><br></pre></td></tr></table></figure>

<h3 id="数组转为-Map"><a href="#数组转为-Map" class="headerlink" title="数组转为 Map"></a>数组转为 Map</h3><p>将数组传入 Map 构造函数，就可以转为 Map。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">new</span> <span class="built_in">Map</span>([</span><br><span class="line">    [<span class="literal">true</span>,<span class="number">7</span>],</span><br><span class="line">    [&#123;<span class="attr">foo</span>:<span class="number">3</span>&#125;,[<span class="string">'abc'</span>]]</span><br><span class="line">])</span><br><span class="line"></span><br><span class="line"><span class="comment">// Map &#123;</span></span><br><span class="line"><span class="comment">//   true =&gt; 7,</span></span><br><span class="line"><span class="comment">//   Object &#123;foo: 3&#125; =&gt; ['abc']</span></span><br><span class="line"><span class="comment">// &#125;</span></span><br></pre></td></tr></table></figure>

<h3 id="Map-转为对象"><a href="#Map-转为对象" class="headerlink" title="Map 转为对象"></a>Map 转为对象</h3><p>如果所有 Map 的键都是字符串，它可以无损地转为对象。如果有非字符串的键名，那么这个键名会被转为字符串，再作为对象的键名。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">strMapToObj</span>(<span class="params">strMap</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">let</span> obj = <span class="built_in">Object</span>.create(<span class="literal">null</span>);</span><br><span class="line">  <span class="keyword">for</span> (<span class="keyword">let</span> [k,v] <span class="keyword">of</span> strMap) &#123;</span><br><span class="line">    obj[k] = v;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> obj;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> myMap = <span class="keyword">new</span> <span class="built_in">Map</span>()</span><br><span class="line">  .set(<span class="string">'yes'</span>, <span class="literal">true</span>)</span><br><span class="line">  .set(<span class="string">'no'</span>, <span class="literal">false</span>);</span><br><span class="line">strMapToObj(myMap)</span><br><span class="line"><span class="comment">// &#123; yes: true, no: false &#125;</span></span><br></pre></td></tr></table></figure>

<h3 id="对象转为-Map"><a href="#对象转为-Map" class="headerlink" title="对象转为 Map"></a>对象转为 Map</h3><p>对象转为 Map 可以通过 <code>Object.entries()</code>。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> obj = &#123;<span class="string">'a'</span>:<span class="number">1</span>,<span class="string">'b'</span>:<span class="number">2</span>&#125;;</span><br><span class="line"><span class="keyword">let</span> map = <span class="keyword">new</span> <span class="built_in">Map</span>(<span class="built_in">Object</span>.entries(obj));</span><br></pre></td></tr></table></figure>

<h2 id="WeakMap"><a href="#WeakMap" class="headerlink" title="WeakMap"></a>WeakMap</h2><h3 id="简介-1"><a href="#简介-1" class="headerlink" title="简介"></a>简介</h3><p>WeakMap 结构与 Map 结构类似，也是用于生成键值对的集合。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// WeakMap 可以使用 set 方法添加成员</span></span><br><span class="line">cosnt wm1 = <span class="keyword">new</span> <span class="built_in">WeakMap</span>();</span><br><span class="line"><span class="keyword">const</span> key = &#123;<span class="attr">foo</span>:<span class="number">1</span>&#125;;</span><br><span class="line">wm1.set(key,<span class="number">2</span>);</span><br><span class="line">wm1.get(key) <span class="comment">// 2</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// WeakMap 也可以接受一个数组</span></span><br><span class="line"><span class="comment">// 作为构造函数的参数</span></span><br><span class="line"><span class="keyword">const</span> k1 = [<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>];</span><br><span class="line"><span class="keyword">const</span> k2 = [<span class="number">4</span>,<span class="number">5</span>,<span class="number">6</span>];</span><br><span class="line"><span class="keyword">const</span> wm2 = <span class="keyword">new</span> <span class="built_in">WeakMap</span>([[k1,<span class="string">'foo'</span>],[k2,<span class="string">'bar'</span>]]);</span><br><span class="line">wm2.get(k2) <span class="comment">// 'bar'</span></span><br></pre></td></tr></table></figure>

<p>WeakMap 与 Map 区别有两点：</p>
<ol>
<li>WeakMap 只接受对象作为键名（<code>null</code> 除外），不接受其他类型的值作为键名；</li>
<li>WeakMap 的键名指向的对象，不计入垃圾回收机制。</li>
</ol>
<p>基本上，如果你要往对象上添加数据，又不想干扰垃圾回收机制，就可以使用 WeakMap 。一个典型的应用场景就是，在网页 DOM 元素上添加数据，就可以使用 WeakMap 结构。当该 DOM 元素被清除，其所对应的 WeakMap 记录就会自动被移除。</p>
<p>WeakMap 的专用场合就是，它的键所对应的对象，可能会在将来消失。WeakMap 结构有助于防止内存泄漏。</p>
<h3 id="语法-1"><a href="#语法-1" class="headerlink" title="语法"></a>语法</h3><p>WeakMap 与 Map 在 API 上的区别主要是两个，一是没有遍历操作（即没有 <code>keys()、values()</code> 和 <code>entries()</code> 方法），也没有 <code>size</code> 属性。因为没有办法列出所有键名，某个键名是否存在完全不可预测，跟垃圾回收机制是否运行相关。这一刻可以取到键名，下一刻垃圾回收机制突然运行了，这个键名就没了，为了防止出现不确定性，就统一规定不能取到键名。二是无法清空，即不支持 <code>clear</code> 方法。因此，WeakMap 只有四个方法可用：<code>get()、set()、has()、delete()</code>。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> wm = <span class="keyword">new</span> <span class="built_in">WeakMap</span>();</span><br><span class="line"></span><br><span class="line"><span class="comment">// size、forEach、clear 方法都不存在</span></span><br><span class="line">wm.size <span class="comment">// undefined</span></span><br><span class="line">wm.forEach <span class="comment">// undefined</span></span><br><span class="line">wm.clear <span class="comment">// undefined</span></span><br></pre></td></tr></table></figure>

<h1 id="Proxy"><a href="#Proxy" class="headerlink" title="Proxy"></a>Proxy</h1><h2 id="概述-1"><a href="#概述-1" class="headerlink" title="概述"></a>概述</h2><p>Proxy 用于修改某些操作的默认行为，等同于在语言层面做出修改，所欲属于一种“元编程”，即对编程语言进行编程。</p>
<p>Proxy 可以理解为，在目标对象之前架设一层“拦截”，外界对该对象的访问，都必须先通过这层拦截，因此提供了一种机制，可以对外界对访问进行过滤和改写。Proxy 这个词的原意是代理，用在这里表示由它来“代理”某些操作，可以译为“代理器”。</p>
<p>ES6 原生提供 Proxy 构造函数，用来生成 Proxy 实例。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> proxy = <span class="keyword">new</span> <span class="built_in">Proxy</span>( target, handler );</span><br></pre></td></tr></table></figure>

<p><code>new Proxy()</code> 表示生成一个 Proxy 实例，<code>target</code> 参数表示所要拦截的目标对象，<code>handler</code> 参数也是一个对象，用来定制拦截行为。</p>
<p>拦截读取属性行为的例子：</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> proxy = <span class="keyword">new</span> <span class="built_in">Proxy</span>(&#123;&#125;, &#123;</span><br><span class="line">  <span class="keyword">get</span>: function(target, propKey) &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">35</span>;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line">proxy.time <span class="comment">// 35</span></span><br><span class="line">proxy.name <span class="comment">// 35</span></span><br><span class="line">proxy.title <span class="comment">// 35</span></span><br></pre></td></tr></table></figure>

<p>上述案例中，作为构造函数，Proxy 接受两个参数。第一个参数是所要代理的目标对象（上例是一个空对象），即如果没有 Proxy  的介入，操作原来要访问的就是这个对象；第二个参数是一个配置对象，对于每一个被代理的操作，需要提供一个对应的处理函数，该函数将拦截对应的操作。比如，上面的代码中，配置对象有一个 <code>get</code> 方法，用来拦截目标对象属性的访问请求。<code>get</code> 方法的两个参数分别是目标对象和所要访问的属性。可以看到，由于拦截函数总是返回 <code>35</code>，所以访问任何属性都得到 <code>35</code>。</p>
<p>要使得 Proxy 起作用，必须针对 Proxy 实例（上例是 Proxy 对象）进行操作，而不是针对目标对象（上例是空对象）进行操作。</p>
<p>如果 <code>handler</code> 没有设置任何拦截，那就等同于直接通向原对象。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> target = &#123;&#125;;</span><br><span class="line"><span class="keyword">var</span> handler = &#123;&#125;;</span><br><span class="line"><span class="keyword">var</span> proxy = <span class="keyword">new</span> <span class="built_in">Proxy</span>(target, handler);</span><br><span class="line">proxy.a = <span class="string">'b'</span>;</span><br><span class="line">target.a <span class="comment">// "b"</span></span><br></pre></td></tr></table></figure>

<p>一个技巧是将 Proxy 对象，设置到 <code>object.proxy</code> 属性，从而可以在 <code>object</code> 对象上调用。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> object = &#123; <span class="attr">proxy</span>: <span class="keyword">new</span> <span class="built_in">Proxy</span>(target, handler) &#125;;</span><br></pre></td></tr></table></figure>


<p>Proxy 实例也可以作为其他对象的原型对象。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> proxy = <span class="keyword">new</span> <span class="built_in">Proxy</span>(&#123;&#125;, &#123;</span><br><span class="line">  <span class="keyword">get</span>: function(target, propKey) &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">35</span>;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> obj = <span class="built_in">Object</span>.create(proxy);</span><br><span class="line">obj.time <span class="comment">// 35</span></span><br></pre></td></tr></table></figure>

<p>同一个拦截器函数，可以设置多个拦截操作。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> handler = &#123;</span><br><span class="line">  <span class="keyword">get</span>: function(target, name) &#123;</span><br><span class="line">    <span class="keyword">if</span> (name === <span class="string">'prototype'</span>) &#123;</span><br><span class="line">      <span class="keyword">return</span> <span class="built_in">Object</span>.prototype;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="string">'Hello, '</span> + name;</span><br><span class="line">  &#125;,</span><br><span class="line"></span><br><span class="line">  apply: <span class="function"><span class="keyword">function</span>(<span class="params">target, thisBinding, args</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> args[<span class="number">0</span>];</span><br><span class="line">  &#125;,</span><br><span class="line"></span><br><span class="line">  construct: <span class="function"><span class="keyword">function</span>(<span class="params">target, args</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> &#123;<span class="attr">value</span>: args[<span class="number">1</span>]&#125;;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> fproxy = <span class="keyword">new</span> <span class="built_in">Proxy</span>(<span class="function"><span class="keyword">function</span>(<span class="params">x, y</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> x + y;</span><br><span class="line">&#125;, handler);</span><br><span class="line"></span><br><span class="line">fproxy(<span class="number">1</span>, <span class="number">2</span>) <span class="comment">// 1</span></span><br><span class="line"><span class="keyword">new</span> fproxy(<span class="number">1</span>, <span class="number">2</span>) <span class="comment">// &#123;value: 2&#125;</span></span><br><span class="line">fproxy.prototype === <span class="built_in">Object</span>.prototype <span class="comment">// true</span></span><br><span class="line">fproxy.foo === <span class="string">"Hello, foo"</span> <span class="comment">// true</span></span><br></pre></td></tr></table></figure>

<h2 id="Proxy-实例的方法列表"><a href="#Proxy-实例的方法列表" class="headerlink" title="Proxy 实例的方法列表"></a>Proxy 实例的方法列表</h2><p>下列是 Proxy 支持的拦截操作一览表，一共 13 种：</p>
<ul>
<li><code>get(target, propKey, receiver)</code>：拦截对象属性的读取，比如 <code>proxy.foo</code> 和 <code>proxy[&#39;foo&#39;]</code>。</li>
<li><code>set(target, propKey, value, receiver)</code>：拦截对象属性的设置，比如 <code>proxy.foo = v</code> 或 <code>proxy[&#39;foo&#39;] = v</code>，返回一个布尔值。</li>
<li><code>has(target, propKey)</code>：拦截 <code>propKey in proxy</code> 的操作，返回一个布尔值。</li>
<li><code>deleteProperty(target, propKey)</code>：拦截 <code>delete proxy[propKey]</code> 的操作，返回一个布尔值。</li>
<li><code>ownKeys(target)</code>：拦截 <code>Object.getOwnPropertyNames(proxy)、Object.getOwnPropertySymbols(proxy)、Object.keys(proxy)、for...in</code> 循环，返回一个数组。该方法返回目标对象所有自身的属性的属性名，而 <code>Object.keys()</code> 的返回结果仅包括目标对象自身的可遍历属性。</li>
<li><code>getOwnPropertyDescriptor(target, propKey)</code>：拦截 <code>Object.getOwnPropertyDescriptor(proxy, propKey)</code>，返回属性的描述对象。</li>
<li><code>defineProperty(target, propKey, propDesc)</code>：拦截 <code>Object.defineProperty(proxy, propKey, propDesc）、Object.defineProperties(proxy, propDescs)</code>，返回一个布尔值。</li>
<li><code>preventExtensions(target)</code>：拦截 <code>Object.preventExtensions(proxy)</code>，返回一个布尔值。</li>
<li><code>getPrototypeOf(target)</code>：拦截 <code>Object.getPrototypeOf(proxy)</code>，返回一个对象。</li>
<li><code>isExtensible(target)</code>：拦截 <code>Object.isExtensible(proxy)</code>，返回一个布尔值。</li>
<li><code>setPrototypeOf(target, proto)</code>：拦截 <code>Object.setPrototypeOf(proxy, proto)</code>，返回一个布尔值。如果目标对象是函数，那么还有两种额外操作可以拦截。</li>
<li><code>apply(target, object, args)</code>：拦截 <code>Proxy</code> 实例作为函数调用的操作，比如 <code>proxy(...args)、proxy.call(object, ...args)、proxy.apply(...)</code>。</li>
<li><code>construct(target, args)</code>：拦截 Proxy 实例作为构造函数调用的操作，比如 <code>new proxy(...args)</code>。</li>
</ul>
<h2 id="Proxy-实例方法详解（仅列举部分常用方法）"><a href="#Proxy-实例方法详解（仅列举部分常用方法）" class="headerlink" title="Proxy 实例方法详解（仅列举部分常用方法）"></a>Proxy 实例方法详解（仅列举部分常用方法）</h2><h3 id="get"><a href="#get" class="headerlink" title="get()"></a>get()</h3><p><code>get</code> 方法用于拦截某个属性的读取操作，可以接受三个参数，依次为目标对象、属性名和 proxy 实例本身（严格地说，是操作行为所针对的对象），其中最后一个参数可选。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> person = &#123;</span><br><span class="line">  name: <span class="string">"张三"</span></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> proxy = <span class="keyword">new</span> <span class="built_in">Proxy</span>(person, &#123;</span><br><span class="line">  <span class="keyword">get</span>: function(target, propKey) &#123;</span><br><span class="line">    <span class="keyword">if</span> (propKey <span class="keyword">in</span> target) &#123;</span><br><span class="line">      <span class="keyword">return</span> target[propKey];</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">      <span class="keyword">throw</span> <span class="keyword">new</span> <span class="built_in">ReferenceError</span>(<span class="string">"Prop name \""</span> + propKey + <span class="string">"\" does not exist."</span>);</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line">proxy.name <span class="comment">// "张三"</span></span><br><span class="line">proxy.age <span class="comment">// 抛出一个错误</span></span><br></pre></td></tr></table></figure>

<p><code>get</code> 方法可以继承。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> proto = <span class="keyword">new</span> <span class="built_in">Proxy</span>(&#123;&#125;,&#123;</span><br><span class="line">    <span class="keyword">get</span>(target,propertyKey,receiver)&#123;</span><br><span class="line">        <span class="built_in">console</span>.log(<span class="string">'GET'</span> + propertyKey);</span><br><span class="line">        <span class="keyword">return</span> target[propertyKey];</span><br><span class="line">    &#125;</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> obj = <span class="built_in">Object</span>.create(proto);</span><br><span class="line">obj.foo <span class="comment">// "GET foo"</span></span><br></pre></td></tr></table></figure>

<p>如果一个属性不可配置（configurable）且不可写（writable），则 Proxy 不能修改属性，否则通过 Proxy 对象访问该属性会报错。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> target = <span class="built_in">Object</span>.defineProperties(&#123;&#125;, &#123;</span><br><span class="line">  foo: &#123;</span><br><span class="line">    value: <span class="number">123</span>,</span><br><span class="line">    writable: <span class="literal">false</span>,</span><br><span class="line">    configurable: <span class="literal">false</span></span><br><span class="line">  &#125;,</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> handler = &#123;</span><br><span class="line">  <span class="keyword">get</span>(target, propKey) &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="string">'abc'</span>;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> proxy = <span class="keyword">new</span> <span class="built_in">Proxy</span>(target, handler);</span><br><span class="line"></span><br><span class="line">proxy.foo</span><br><span class="line"><span class="comment">// TypeError: Invariant check failed</span></span><br></pre></td></tr></table></figure>

<h3 id="set"><a href="#set" class="headerlink" title="set()"></a>set()</h3><p><code>set</code> 方法用来拦截某个属性的赋值操作，可以接受四个参数，依次为目标对象、属性名、属性值和 Proxy 实例本身，其中最后一个参数可选。</p>
<p>假如 <code>Person</code> 对象有一个 <code>age</code> 属性，该属性应该是一个不大于 <code>200</code> 的整数，那么可以使用 Proxy 保证 <code>age</code> 的属性值符合要求。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> validator = &#123;</span><br><span class="line">  <span class="keyword">set</span>: function(obj, prop, value) &#123;</span><br><span class="line">    <span class="keyword">if</span> (prop === <span class="string">'age'</span>) &#123;</span><br><span class="line">      <span class="keyword">if</span> (!<span class="built_in">Number</span>.isInteger(value)) &#123;</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> <span class="built_in">TypeError</span>(<span class="string">'The age is not an integer'</span>);</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">if</span> (value &gt; <span class="number">200</span>) &#123;</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> <span class="built_in">RangeError</span>(<span class="string">'The age seems invalid'</span>);</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 对于满足条件的 age 属性以及其他属性，直接保存</span></span><br><span class="line">    obj[prop] = value;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> person = <span class="keyword">new</span> <span class="built_in">Proxy</span>(&#123;&#125;, validator);</span><br><span class="line"></span><br><span class="line">person.age = <span class="number">100</span>;</span><br><span class="line"></span><br><span class="line">person.age <span class="comment">// 100</span></span><br><span class="line">person.age = <span class="string">'young'</span> <span class="comment">// 报错</span></span><br><span class="line">person.age = <span class="number">300</span> <span class="comment">// 报错</span></span><br></pre></td></tr></table></figure>

<p>有时，我们会在对象上面设置内部属性，属性名的第一个字符使用下划线开头，表示这些属性不应该被外部使用。结合 <code>get</code> 和 <code>set</code> 方法，可以做到防止内部属性被外界读写。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> handler = &#123;</span><br><span class="line">  <span class="keyword">get</span> (target, key) &#123;</span><br><span class="line">    invariant(key, <span class="string">'get'</span>);</span><br><span class="line">    <span class="keyword">return</span> target[key];</span><br><span class="line">  &#125;,</span><br><span class="line">  <span class="keyword">set</span> (target, key, value) &#123;</span><br><span class="line">    invariant(key, <span class="string">'set'</span>);</span><br><span class="line">    target[key] = value;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;;</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">invariant</span> (<span class="params">key, action</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">if</span> (key[<span class="number">0</span>] === <span class="string">'_'</span>) &#123;</span><br><span class="line">    <span class="keyword">throw</span> <span class="keyword">new</span> <span class="built_in">Error</span>(<span class="string">`Invalid attempt to <span class="subst">$&#123;action&#125;</span> private "<span class="subst">$&#123;key&#125;</span>" property`</span>);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">const</span> target = &#123;&#125;;</span><br><span class="line"><span class="keyword">const</span> proxy = <span class="keyword">new</span> <span class="built_in">Proxy</span>(target, handler);</span><br><span class="line">proxy._prop</span><br><span class="line"><span class="comment">// Error: Invalid attempt to get private "_prop" property</span></span><br><span class="line">proxy._prop = <span class="string">'c'</span></span><br><span class="line"><span class="comment">// Error: Invalid attempt to set private "_prop" property</span></span><br></pre></td></tr></table></figure>

<p>其余拦截方法略，详细用法翻阅相关文档。</p>
<h1 id="Reflect"><a href="#Reflect" class="headerlink" title="Reflect"></a>Reflect</h1><h2 id="概述-2"><a href="#概述-2" class="headerlink" title="概述"></a>概述</h2><p><code>Reflect</code> 对象设计目的：</p>
<ul>
<li>将 <code>Object</code> 对象的一些明显属于语言内部的方法（比如 <code>Object.defineProperty</code>），放到 <code>Reflect</code> 对象上。现阶段，某些方法同时在 <code>Object</code> 和 <code>Reflect</code> 对象上部署，未来的新方法只部署在 <code>Reflect</code> 对象上。也就是说，从 <code>Reflect</code> 对象上可以拿到对象内部的方法。</li>
<li>修改某些 <code>Object</code> 方法的返回结果，让其变得合理。比如，<code>Object.defineProperty(obj，name，desc)</code>  在无法定义属性时，会报错，而 <code>Reflect.defineProperty(obj，name，desc)</code> 则会返回 <code>false</code>。</li>
<li>让 <code>Object</code> 操作都变成为函数行为。某些 <code>Object</code> 操作为命令式，比如 <code>name in obj</code> 和 <code>delete obj[name]</code>，而 <code>Reflect.has(obj，name)</code> 和 <code>Reflct.deleteProperty(obj，name)</code> 让他们变成了函数行为。</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 老写法</span></span><br><span class="line"><span class="string">'assign'</span> <span class="keyword">in</span> <span class="built_in">Object</span> <span class="comment">// true</span></span><br><span class="line"><span class="comment">// 新写法</span></span><br><span class="line"><span class="built_in">Reflect</span>.has(<span class="built_in">Object</span>,<span class="string">'assign) // true</span></span><br></pre></td></tr></table></figure>

<ul>
<li><code>Reflect</code> 对象的方法与 <code>Proxy</code> 对象的方法一一对应，只要是 <code>Proxy</code> 对象的方法，就能在 <code>Reflect</code> 对象上找到对应的方法。这就让 <code>Proxy</code> 对象可以方便的调用对应的 <code>Reflect</code> 方法，完成默认行为，作为修改行为的基础。也就是说，不管 <code>Proxy</code> 怎么修改默认行为，你总可以在 <code>Reflect</code> 上获取默认行为。</li>
</ul>
<p>有了 Reflect 对象操作后，很多操作会更易读。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 老写法</span></span><br><span class="line">Fucntion.prorotype.apply.call(<span class="built_in">Math</span>.floor,<span class="literal">undefined</span>,[<span class="number">1.75</span>]) <span class="comment">// 1</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 新写法</span></span><br><span class="line"><span class="built_in">Reflect</span>.apply(<span class="built_in">Math</span>.floor,<span class="literal">undefined</span>,[<span class="number">1.75</span>]) <span class="comment">// 1</span></span><br></pre></td></tr></table></figure>

<h2 id="静态方法"><a href="#静态方法" class="headerlink" title="静态方法"></a>静态方法</h2><p>Reflect 对象一共有 13 个静态方法：</p>
<ul>
<li>Reflect.apply(target, thisArg, args)</li>
<li>Reflect.construct(target, args)</li>
<li>Reflect.get(target, name, receiver)</li>
<li>Reflect.set(target, name, value, receiver)</li>
<li>Reflect.defineProperty(target, name, desc)</li>
<li>Reflect.deleteProperty(target, name)</li>
<li>Reflect.has(target, name)</li>
<li>Reflect.ownKeys(target)</li>
<li>Reflect.isExtensible(target)</li>
<li>Reflect.preventExtensions(target)</li>
<li>Reflect.getOwnPropertyDescriptor(target, name)</li>
<li>Reflect.getPrototypeOf(target)</li>
<li>Reflect.setPrototypeOf(target, prototype)</li>
</ul>
<p>大部分与 <code>Object</code> 对象的同名方法作用都是相同的，而且它与 <code>Proxy</code> 对象的方法都是一一对应的。</p>
<h3 id="Reflect-get-target，name，receiver"><a href="#Reflect-get-target，name，receiver" class="headerlink" title="Reflect.get(target，name，receiver)"></a>Reflect.get(target，name，receiver)</h3><p><code>Refelct.get</code> 方法查找并返回 <code>target</code> 对象的 <code>name</code> 属性，如果没有该属性，则返回 <code>undefined</code>。如果第一个参数不是对象，<code>Reflect.get</code> 方法会报错。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> obj = &#123;</span><br><span class="line">    a:<span class="number">1</span>,</span><br><span class="line">    b:<span class="number">2</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">Reflect</span>.get(obj,<span class="string">'a'</span>) <span class="comment">// 1</span></span><br><span class="line"><span class="built_in">Reflect</span>.get(<span class="number">1</span>,<span class="string">'b'</span>) <span class="comment">// 报错</span></span><br></pre></td></tr></table></figure>

<h3 id="Reflect-set-target，name，value，receiver"><a href="#Reflect-set-target，name，value，receiver" class="headerlink" title="Reflect.set(target，name，value，receiver)"></a>Reflect.set(target，name，value，receiver)</h3><p><code>Reflect.set</code> 方法设置 <code>target</code> 对象的 <code>name</code> 属性等于 <code>value</code>。如果第一个参数不是对象，<code>Reflect.set</code> 方法会报错。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> obj = &#123;</span><br><span class="line">    a: <span class="number">1</span>,</span><br><span class="line">    b: <span class="number">2</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">Reflect</span>.set(obj,<span class="string">'c'</span>,<span class="number">3</span>)</span><br><span class="line"><span class="built_in">console</span>.log(obj); <span class="comment">// &#123; a: 1, b: 2, c: 3 &#125;</span></span><br></pre></td></tr></table></figure>

<h3 id="Reflect-has-obj，name"><a href="#Reflect-has-obj，name" class="headerlink" title="Reflect.has(obj，name)"></a>Reflect.has(obj，name)</h3><p><code>Reflect.has</code> 方法对应 <code>name in obj</code> 里面的 <code>in</code> 运算符。如果第一个参数不是对象，<code>Reflect.has</code> 方法会报错。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> obj = &#123;</span><br><span class="line">    a: <span class="number">1</span>,</span><br><span class="line">    b: <span class="number">2</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 旧写法</span></span><br><span class="line"><span class="built_in">console</span>.log(<span class="string">'a'</span> <span class="keyword">in</span> obj); <span class="comment">// true</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 新写法</span></span><br><span class="line"><span class="built_in">console</span>.log(<span class="built_in">Reflect</span>.has(obj, <span class="string">'b'</span>)); <span class="comment">// true</span></span><br></pre></td></tr></table></figure>

<h3 id="Reflect-deleteProperty-obj，name"><a href="#Reflect-deleteProperty-obj，name" class="headerlink" title="Reflect.deleteProperty(obj，name)"></a>Reflect.deleteProperty(obj，name)</h3><p><code>Reflect.deleteProperty</code> 方法等同于 <code>delete obj[name]</code> ，用于删除对象的属性。第一个参数不是对象，会报错。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> obj = &#123;</span><br><span class="line">    a: <span class="number">1</span>,</span><br><span class="line">    b: <span class="number">2</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(obj); <span class="comment">// &#123; a: 1, b: 2 &#125;</span></span><br><span class="line"><span class="built_in">console</span>.log(<span class="built_in">Reflect</span>.deleteProperty(obj, <span class="string">'a'</span>)); <span class="comment">// true</span></span><br><span class="line"><span class="built_in">console</span>.log(obj); <span class="comment">// &#123; b: 2 &#125;</span></span><br></pre></td></tr></table></figure>

<h3 id="Reflect-construct-target，args"><a href="#Reflect-construct-target，args" class="headerlink" title="Reflect.construct(target，args)"></a>Reflect.construct(target，args)</h3><p><code>Reflect.construct</code> 方法等同于 <code>new target（...args）</code>，这提供了一种不使用 <code>new</code>，来调用构造函数的方法。如果 <code>Reflect.construct()</code> 方法的第一个参数不是函数，会报错。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Greeting</span>(<span class="params">name</span>)</span>&#123;</span><br><span class="line">    <span class="keyword">this</span>.name = name;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// new 的写法</span></span><br><span class="line"><span class="keyword">const</span> instance = <span class="keyword">new</span> Greeting(<span class="string">'张三'</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">// Reflect.construct 的写法</span></span><br><span class="line"><span class="keyword">const</span> instance = <span class="built_in">Reflect</span>.construct(Greeting,[<span class="string">'张三'</span>])</span><br></pre></td></tr></table></figure>

<h3 id="Reflect-getPrototypeOf-obj"><a href="#Reflect-getPrototypeOf-obj" class="headerlink" title="Reflect.getPrototypeOf(obj)"></a>Reflect.getPrototypeOf(obj)</h3><p><code>Reflect.getPrototypeOf</code> 方法用于读取对象的 <code>__proto__</code> 属性，对应 <code>Object.getPrototypeOf(obj)</code>。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> myObj = <span class="keyword">new</span> FancyThing();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 旧写法</span></span><br><span class="line"><span class="built_in">Object</span>.getPrototypeOf(myObj) === FancyThing.prototype;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 新写法</span></span><br><span class="line"><span class="built_in">Reflect</span>.getPrototypeOf(myObj) === FancyThing.prototype;</span><br></pre></td></tr></table></figure>

<p>两个用法区别在于，如果参数不是对象，<code>Object.getPrototypeOf</code> 会将这个参数转为对象，然后运行，而 <code>Reflect.getPrototypeOf</code> 会报错。</p>
<h3 id="Reflect-setPrototypeOf-obj，newProto"><a href="#Reflect-setPrototypeOf-obj，newProto" class="headerlink" title="Reflect.setPrototypeOf(obj，newProto)"></a>Reflect.setPrototypeOf(obj，newProto)</h3><p><code>Reflect.setPrototypeOf</code> 方法用来设置目标对象的原型，对应 <code>Object.setPrototypeOf()</code> 方法。它返回一个布尔值，表示是否设置成功。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> myObj = &#123;&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 旧写法</span></span><br><span class="line"><span class="built_in">Object</span>.setProrotype(myObj,<span class="built_in">Array</span>.prototype);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 新写法</span></span><br><span class="line"><span class="built_in">Reflect</span>.setPrototypeOf(myObj,<span class="built_in">Array</span>.prototype);</span><br><span class="line"></span><br><span class="line"><span class="comment">// myObj.length // 0</span></span><br></pre></td></tr></table></figure>

<p>如果无法设置目标对象的原型（比如，禁止扩展），<code>Reflect.setPrototypeOf</code> 方法返回 <code>false</code>。如果第一个参数不是对象，<code>Object.setPrototypeOf</code> 会返回第一个参数本身，而 <code>Reflect.setPrototypeOf</code> 会报错。如果第一个参数是 <code>undefined</code> 或 <code>null</code> ，他们两个都报错。</p>
<h3 id="Reflect-apply-func，thisArg，args"><a href="#Reflect-apply-func，thisArg，args" class="headerlink" title="Reflect.apply(func，thisArg，args)"></a>Reflect.apply(func，thisArg，args)</h3><p><code>Reflect.apply</code> 方法等同于 <code>Function.prototype.apply.call(func，thisArg，args)</code>，用于绑定 <code>this</code> 对象后执行给定函数。</p>
<p>一般来说，如果要绑定一个函数的 <code>this</code> 对象，可以这样写 <code>fn.apply(obj，args)</code>，但是如果函数定义了自己的 <code>apply</code> 方法，就只能写成 <code>Function.prototype.apply.call(fn，obj，args)</code>，采用 <code>Reflect</code> 对象可以简化这种操作。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> ages = [<span class="number">11</span>, <span class="number">33</span>, <span class="number">12</span>, <span class="number">54</span>, <span class="number">18</span>, <span class="number">96</span>];</span><br><span class="line"></span><br><span class="line"><span class="comment">// 旧写法</span></span><br><span class="line"><span class="keyword">const</span> youngest = <span class="built_in">Math</span>.min.apply(<span class="built_in">Math</span>, ages);</span><br><span class="line"><span class="keyword">const</span> oldest = <span class="built_in">Math</span>.max.apply(<span class="built_in">Math</span>, ages);</span><br><span class="line"><span class="keyword">const</span> type = <span class="built_in">Object</span>.prototype.toString.call(youngest);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 新写法</span></span><br><span class="line"><span class="keyword">const</span> youngest = <span class="built_in">Reflect</span>.apply(<span class="built_in">Math</span>.min, <span class="built_in">Math</span>, ages);</span><br><span class="line"><span class="keyword">const</span> oldest = <span class="built_in">Reflect</span>.apply(<span class="built_in">Math</span>.max, <span class="built_in">Math</span>, ages);</span><br><span class="line"><span class="keyword">const</span> type = <span class="built_in">Reflect</span>.apply(<span class="built_in">Object</span>.prototype.toString, youngest, []);</span><br></pre></td></tr></table></figure>

<h3 id="Reflect-defineProperty-target，propertyKey，attributes"><a href="#Reflect-defineProperty-target，propertyKey，attributes" class="headerlink" title="Reflect.defineProperty(target，propertyKey，attributes)"></a>Reflect.defineProperty(target，propertyKey，attributes)</h3><p><code>Reflect.defineProperty</code> 方法基本等同于 <code>Object.defineProperty</code>，用来为对象定义属性。未来，后者会被逐渐废除。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">MyDate</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    ...</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 旧写法</span></span><br><span class="line"><span class="built_in">Object</span>.defineProperty(MyDate,<span class="string">'now'</span>,&#123;</span><br><span class="line">    value:<span class="function"><span class="params">()</span> =&gt;</span> <span class="built_in">Date</span>.now()</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 新写法</span></span><br><span class="line"><span class="built_in">Reflect</span>.defineProperty(MyDate,<span class="string">'now'</span>,&#123;</span><br><span class="line">    value:<span class="function"><span class="params">()</span> =&gt;</span> <span class="built_in">Date</span>.now()</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>

<h3 id="Reflect-getOwnPropertyDescriptor-target，propertyKey"><a href="#Reflect-getOwnPropertyDescriptor-target，propertyKey" class="headerlink" title="Reflect.getOwnPropertyDescriptor(target，propertyKey)"></a>Reflect.getOwnPropertyDescriptor(target，propertyKey)</h3><p><code>Reflect.getOwnPropertyDescriptor</code> 基本等同于 <code>Object.getOwnPropertyDescriptor</code>，用于得到指定属性的描述对象，将来会代替后者。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> myObject = &#123;&#125;;</span><br><span class="line"></span><br><span class="line"><span class="built_in">Object</span>.defineProperty(myObject,<span class="string">'hidden'</span>,&#123;</span><br><span class="line">    value:<span class="literal">true</span>,</span><br><span class="line">    enumerable:<span class="literal">false</span>,</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 旧写法</span></span><br><span class="line"><span class="keyword">var</span> theDescriptor = <span class="built_in">Object</span>.getOwnPropertyDescriptor(myObject,<span class="string">'hidden'</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 新写法</span></span><br><span class="line"><span class="keyword">var</span> theDescriptor = <span class="built_in">Reflect</span>.getOwnPropertyDescriptor(myObject,<span class="string">'hidden'</span>);</span><br></pre></td></tr></table></figure>

<h3 id="Reflect-isExtensible-target"><a href="#Reflect-isExtensible-target" class="headerlink" title="Reflect.isExtensible(target)"></a>Reflect.isExtensible(target)</h3><p><code>Reflect.isExtensible</code> 方法对应 <code>Object.isExtensible</code>，返回一个布尔值，表示当前对象是否可扩展。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> myObject = &#123;&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 旧写法</span></span><br><span class="line"><span class="built_in">Object</span>.isExtensible(myObject) <span class="comment">// true</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 新写法</span></span><br><span class="line"><span class="built_in">Object</span>.isExtensible(myObject) <span class="comment">// true</span></span><br></pre></td></tr></table></figure>

<h3 id="Reflect-preventExtensions-target"><a href="#Reflect-preventExtensions-target" class="headerlink" title="Reflect.preventExtensions(target)"></a>Reflect.preventExtensions(target)</h3><p><code>Reflect.preventExtensions</code> 对应 <code>Object.preventExtensions</code> 方法，用于让一个对象变为不可扩展。它返回一个布尔值，表示是否操作成功。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> myObject = &#123;&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 旧写法</span></span><br><span class="line">Obect.preventExtensions(myObject) <span class="comment">// Object&#123;&#125;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 新写法</span></span><br><span class="line"><span class="built_in">Reflect</span>.preventExtensions(myObject) <span class="comment">// true</span></span><br></pre></td></tr></table></figure>

<h3 id="Reflect-ownKeys-target"><a href="#Reflect-ownKeys-target" class="headerlink" title="Reflect.ownKeys(target)"></a>Reflect.ownKeys(target)</h3><p><code>Reflect.ownKeys</code> 方法用于返回对象的所有属性，基本等同于 <code>Object.getOwnPropertyNames</code> 与 <code>Object.getOwnPropertySymbols</code> 之和。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> myObject = &#123;</span><br><span class="line">    foo:<span class="number">1</span>,</span><br><span class="line">    bar:<span class="number">2</span>,</span><br><span class="line">    [<span class="built_in">Symbol</span>.for(<span class="string">'baz'</span>)]:<span class="number">3</span>,</span><br><span class="line">    [<span class="built_in">Symbol</span>.for(<span class="string">'bing'</span>)]:<span class="number">4</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 旧写法</span></span><br><span class="line"><span class="built_in">Object</span>.getOwnPropertyNames(myObject) <span class="comment">// ['foo','bar']</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">Object</span>.getOwnPropertySymbols(myObject) <span class="comment">// [Symbol(baz),Symbol(bing)]</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 新写法</span></span><br><span class="line"><span class="built_in">Reflect</span>.ownKeys(myObject) <span class="comment">// ['foo','bar',Symbol(baz),Symbol(bing)]</span></span><br></pre></td></tr></table></figure>

<h1 id="Promise-对象"><a href="#Promise-对象" class="headerlink" title="Promise 对象"></a>Promise 对象</h1><h2 id="Promise-含义"><a href="#Promise-含义" class="headerlink" title="Promise 含义"></a>Promise 含义</h2><p>所谓 <code>Promise</code>，简单来说就是一个容器，里面保存着某个未来才会结束的事件（通常是一个异步操作）的结果。</p>
<p><code>Promise</code> 对象有两个特点：</p>
<ul>
<li><p>对象的状态不受外界影响。==<code>Promise</code> 对象代表一个异步操作，有三种状态：<code>pending（进行中）、fulfilled（已成功）和 rejected（已失败）</code>。== 只有异步操作的结果，可以决定当前是哪一种状态，任何其他操作都无法改变这个状态。</p>
</li>
<li><p>一旦状态改变，就不会再变，任何时候都可以得到这个结果。<code>Promise</code> 对象的状态改变，只有两种可能：从 <code>pending</code> 变为 <code>fulfilled</code> 和从 <code>pending</code> 变为 <code>rejected</code>。只要这两种情况发生，状态就凝固了，不会再变了，会一直保持这个结果，这时就称为 <code>resolved（已定型）</code>。如果改变已经发生了，你再对 <code>Promise</code> 对象添加回调函数，也会立即得到这个结果。这与事件（<code>Event</code>）完全不同，事件的特点是，如果你错过了它，再去监听，是得不到结果的。</p>
</li>
</ul>
<p><code>Promise</code> 对象可以将异步操作以同步操作的流程表达出来，避免了层层嵌套的回调函数。它也有一些缺点：</p>
<ul>
<li>无法取消 <code>Promise</code>，一旦新建它就会立即执行，无法中途取消。</li>
<li>如果不设置回调函数，Promise 内部抛出的错误，不会反应到外部。</li>
<li>当处于 <code>pending</code> 状态时，无法得知目前进展到哪一个阶段（刚开始还是即将完成）。</li>
</ul>
<h2 id="基本用法-2"><a href="#基本用法-2" class="headerlink" title="基本用法"></a>基本用法</h2><p>==<code>Promise</code> 对象是一个构造函数，用来生成 <code>Promise</code> 实例。==</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> promise = <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function"><span class="keyword">function</span>(<span class="params">resolve, reject</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// ... some code</span></span><br><span class="line"></span><br><span class="line">  <span class="keyword">if</span> (<span class="comment">/* 异步操作成功 */</span>)&#123;</span><br><span class="line">    resolve(value);</span><br><span class="line">  &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    reject(error);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>

<p><code>Promise</code> 构造函数接受一个函数作为参数，该函数的两个参数分别是 <code>resolve</code> 和 <code>reject</code>。它们是两个函数，由 JavaScript 引擎提供，不用自己部署。</p>
<p><code>resolve</code> 函数作用是，将 <code>Promise</code> 对象的状态从 “未完成” 变成 “成功”（即从 <code>pending</code> 变为 <code>resolved</code>），在异步操作成功时调用，并将异步操作的结果，作为参数传递出去；<code>reject</code> 函数的作用是，将 <code>Promise</code> 对象的状态从 “未完成” 变为 “失败”（即从 <code>pending</code> 变为 <code>rejected</code>），在异步操作失败的时候调用，并将异步操作报出的错误，作为参数传递出去。</p>
<p><code>Promise</code> 实例生成后，可以用 <code>then</code> 方法分别指定 <code>resolved</code> 状态和 <code>rejected</code> 状态的回调函数。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">promise.then(<span class="function"><span class="keyword">function</span>(<span class="params">value</span>)</span>&#123;</span><br><span class="line">    <span class="comment">// success</span></span><br><span class="line">&#125;,<span class="function"><span class="keyword">function</span>(<span class="params">error</span>)</span>&#123;</span><br><span class="line">    <span class="comment">// failure</span></span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>

<p><code>then</code> 方法可以接受两个回调函数作为参数。第一个回调函数是 <code>Promise</code> 对象的状态变为 <code>resolved</code> 时调用，第二个回调函数是 <code>Promise</code> 对象的状态变为 <code>rejected</code> 时调用。其中，第二个函数是可选的，不一定要提供。这两个函数都接受 <code>Promise</code> 对象传出的值作为参数。</p>
<p><code>Promise</code> 新建后就会立即执行。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> promise = <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function"><span class="keyword">function</span>(<span class="params">resolve,reject</span>)</span>&#123;</span><br><span class="line">    consoel.log(<span class="string">'Promise'</span>);</span><br><span class="line">    resolve()</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line">promise.then(<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">'resolved.'</span>);</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(<span class="string">'Hi!'</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// Promise</span></span><br><span class="line"><span class="comment">// Hi!</span></span><br><span class="line"><span class="comment">// resolved.</span></span><br></pre></td></tr></table></figure>

<p><code>Promise</code> 对象实现 <code>Ajax</code> 操作。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> getJSON = <span class="function"><span class="keyword">function</span>(<span class="params">url</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">const</span> promise = <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function"><span class="keyword">function</span>(<span class="params">resolve, reject</span>)</span>&#123;</span><br><span class="line">    <span class="keyword">const</span> handler = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">      <span class="keyword">if</span> (<span class="keyword">this</span>.readyState !== <span class="number">4</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="keyword">if</span> (<span class="keyword">this</span>.status === <span class="number">200</span>) &#123;</span><br><span class="line">        resolve(<span class="keyword">this</span>.response);</span><br><span class="line">      &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        reject(<span class="keyword">new</span> <span class="built_in">Error</span>(<span class="keyword">this</span>.statusText));</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;;</span><br><span class="line">    <span class="keyword">const</span> client = <span class="keyword">new</span> XMLHttpRequest();</span><br><span class="line">    client.open(<span class="string">"GET"</span>, url);</span><br><span class="line">    client.onreadystatechange = handler;</span><br><span class="line">    client.responseType = <span class="string">"json"</span>;</span><br><span class="line">    client.setRequestHeader(<span class="string">"Accept"</span>, <span class="string">"application/json"</span>);</span><br><span class="line">    client.send();</span><br><span class="line"></span><br><span class="line">  &#125;);</span><br><span class="line"></span><br><span class="line">  <span class="keyword">return</span> promise;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">getJSON(<span class="string">"/posts.json"</span>).then(<span class="function"><span class="keyword">function</span>(<span class="params">json</span>) </span>&#123;</span><br><span class="line">  <span class="built_in">console</span>.log(<span class="string">'Contents: '</span> + json);</span><br><span class="line">&#125;, <span class="function"><span class="keyword">function</span>(<span class="params">error</span>) </span>&#123;</span><br><span class="line">  <span class="built_in">console</span>.error(<span class="string">'出错了'</span>, error);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>

<p>一般来说，调用 <code>resolve</code> 或者 <code>reject</code> 以后，<code>Promise</code> 的使命就完成了，后继操作应该放到 <code>then</code> 方法里面，而不应该直接写在 <code>resolve</code> 或者 <code>reject</code> 的后面。所以，最好在它们前面加上 <code>return</code> 语句，这样就不会有意外。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function">(<span class="params">resolve,reject</span>) =&gt;</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> resolve(<span class="number">1</span>);</span><br><span class="line">    <span class="comment">// 后面的语句不会执行</span></span><br><span class="line">    <span class="built_in">console</span>.log(<span class="number">2</span>);</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>

<h2 id="Promise-prototype-then"><a href="#Promise-prototype-then" class="headerlink" title="Promise.prototype.then()"></a>Promise.prototype.then()</h2><p><code>Promise</code> 实例具有 <code>then</code> 方法，<code>then</code> 方法是定义在原型对象 <code>Promise.prototype</code> 上的。它的作用是为 <code>Promise</code> 实例添加状态改变时的回调函数。第一个参数是 <code>resolved</code> 状态的回调函数，第二个参数（可选）是 <code>rejected</code> 状态的回调函数。</p>
<p><code>then</code> 方法返回的是一个新的 <code>Promise</code> 实例（注意，不是原来的那个 <code>Promise</code> 实例）。因此可以采用链式写法，即 <code>then</code> 方法后面再调用另一个 <code>then</code> 方法。</p>
<p>采用链式的 <code>then</code>，可以指定一组按照次序调用的回调函数。这时，前一个回调函数，有可能返回的还是一个 <code>Promise</code> 对象（即有异步操作），这时后一个回调函数，就会等待该 <code>Promise</code> 对象的状态发生变化，才会被调用。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">getJSON(<span class="string">"/post/1.json"</span>).then(<span class="function"><span class="keyword">function</span>(<span class="params">post</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> getJSON(post.commentURL);</span><br><span class="line">&#125;).then(<span class="function"><span class="keyword">function</span> (<span class="params">comments</span>) </span>&#123;</span><br><span class="line">  <span class="built_in">console</span>.log(<span class="string">"resolved: "</span>, comments);</span><br><span class="line">&#125;, <span class="function"><span class="keyword">function</span> (<span class="params">err</span>)</span>&#123;</span><br><span class="line">  <span class="built_in">console</span>.log(<span class="string">"rejected: "</span>, err);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>

<h2 id="Promise-prototype-catch"><a href="#Promise-prototype-catch" class="headerlink" title="Promise.prototype.catch()"></a>Promise.prototype.catch()</h2><p><code>Promise.prototype.catch</code> 方法是 <code>.then(null，rejection)</code> 或 <code>.then(undefined，rejection)</code> 的别名，用于指定发生错误时的回调函数。</p>
<p>一般来说，不要在 <code>then</code> 方法里面定义 <code>Reject</code> 状态的回调函数（即 <code>then</code> 的第二个参数），总是使用 <code>catch</code> 方法。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// bad</span></span><br><span class="line">promise</span><br><span class="line">  .then(<span class="function"><span class="keyword">function</span>(<span class="params">data</span>) </span>&#123;</span><br><span class="line">    <span class="comment">// success</span></span><br><span class="line">  &#125;, <span class="function"><span class="keyword">function</span>(<span class="params">err</span>) </span>&#123;</span><br><span class="line">    <span class="comment">// error</span></span><br><span class="line">  &#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// good</span></span><br><span class="line">promise</span><br><span class="line">  .then(<span class="function"><span class="keyword">function</span>(<span class="params">data</span>) </span>&#123; <span class="comment">//cb</span></span><br><span class="line">    <span class="comment">// success</span></span><br><span class="line">  &#125;)</span><br><span class="line">  .catch(<span class="function"><span class="keyword">function</span>(<span class="params">err</span>) </span>&#123;</span><br><span class="line">    <span class="comment">// error</span></span><br><span class="line">  &#125;);</span><br></pre></td></tr></table></figure>

<p>上面代码中，第二种写法比较好些。理由是第二种写法可以捕获前面 <code>then</code> 方法执行中的错误，也更接近同步的写法（<code>try/catch</code>）。建议总是使用 <code>catch</code> 方法，而不使用 <code>then</code> 方法的第二个参数。而且建议，<code>Promise</code> 对象后面要跟 <code>catch</code> 方法，这样可以处理 <code>Promise</code> 内部发生的错误。<code>catch</code> 方法返回的还是一个 <code>Promise</code> 对象，因此后面还可以接着调用 <code>then</code> 方法。</p>
<h2 id="Promise-prototype-finally"><a href="#Promise-prototype-finally" class="headerlink" title="Promise.prototype.finally()"></a>Promise.prototype.finally()</h2><p><code>finally</code> 方法用于指定不管 <code>Promise</code> 对象最后状态如何，都会执行的操作。该方法是是 ES2018 引入标准的。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">promise</span><br><span class="line">.then(<span class="function"><span class="params">result</span> =&gt;</span> &#123;···&#125;)</span><br><span class="line">.catch(<span class="function"><span class="params">error</span> =&gt;</span> &#123;···&#125;)</span><br><span class="line">.finally(<span class="function"><span class="params">()</span> =&gt;</span> &#123;···&#125;);</span><br></pre></td></tr></table></figure>

<p>上述代码中，不管 <code>Promise</code> 最后的状态，在执行完 <code>then</code> 或 <code>catch</code> 指定的回调函数以后，都会执行 <code>finally</code> 方法指定的回调函数。</p>
<h2 id="Promise-all"><a href="#Promise-all" class="headerlink" title="Promise.all()"></a>Promise.all()</h2><p><code>Promise.all()</code> 方法用于将多个 <code>Promise</code> 实例，包装成一个新的 <code>Promise</code> 实例。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> p = <span class="built_in">Promise</span>.all([p1, p2, p3]);</span><br></pre></td></tr></table></figure>

<p>参数可以不是数组，但必须具有 <code>Iterator</code> 接口，且返回的每个成员都是 <code>Promise</code> 实例。</p>
<p><code>p</code> 的状态由 <code>p1、p2、p3</code> 决定，分两种情况：</p>
<ul>
<li>只有 <code>p1、p2、p3</code> 的状态都变成 <code>fulfilled</code>，<code>p</code> 的状态才会变成 <code>fulfulled</code>，此时 <code>p1、p2、p3</code> 的返回值组成一个数组，传递给 <code>p</code> 的回调函数。</li>
<li>只要 <code>p1、p2、p3</code> 之中有一个被 <code>rejected</code>，<code>p</code> 的状态就变成 <code>rejected</code>，此时第一个被 <code>reject</code> 的实例的返回值，会传递给 <code>p</code> 的回调函数。</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 生成一个Promise对象的数组</span></span><br><span class="line"><span class="keyword">const</span> promises = [<span class="number">2</span>, <span class="number">3</span>, <span class="number">5</span>, <span class="number">7</span>, <span class="number">11</span>, <span class="number">13</span>].map(<span class="function"><span class="keyword">function</span> (<span class="params">id</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> getJSON(<span class="string">'/post/'</span> + id + <span class="string">".json"</span>);</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="built_in">Promise</span>.all(promises).then(<span class="function"><span class="keyword">function</span> (<span class="params">posts</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">&#125;).catch(<span class="function"><span class="keyword">function</span>(<span class="params">reason</span>)</span>&#123;</span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>

<p>上面案例，promises 是包含 6 个 Promise 实例的数组，只有这 6 个实例的状态都变成 fulfilled ，或者其中一个变为 rejected ，才会调用 Promise.all 方法后面的回调函数。</p>
<h2 id="Promise-race"><a href="#Promise-race" class="headerlink" title="Promise.race()"></a>Promise.race()</h2><p><code>Promise.race()</code> 方法同样是将多个 <code>Promise</code>  实例，包装成一个新的 <code>Promise</code> 实例。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> p = <span class="built_in">Promise</span>.race([p1, p2, p3]);</span><br></pre></td></tr></table></figure>

<p>上述代码，只要 <code>p1、p2、p3</code> 之中有一个实例率先改变状态，<code>p</code> 的状态就跟着改变。那个率先改变 <code>Promise</code> 实例的返回值，就传递给 <code>p</code> 的回调函数。</p>
<h2 id="Promise-resolve"><a href="#Promise-resolve" class="headerlink" title="Promise.resolve()"></a>Promise.resolve()</h2><p><code>Promise.resolve()</code> 方法用于将现有对象转为 <code>Promise</code> 对象。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> jsPromise = <span class="built_in">Promise</span>.resolve($.ajax(<span class="string">'/whatever.json'</span>));</span><br></pre></td></tr></table></figure>

<p><code>Promise.resolve</code> 方法参数分四种情况：</p>
<ul>
<li>参数是一个 <code>Promise</code> 实例</li>
</ul>
<p>如果参数是一个 Promise 实例，那么 <code>Promise.resolve</code>  将不做任何修改、原封不动地返回这个实例。</p>
<ul>
<li>参数是一个 <code>thenable</code> 对象</li>
</ul>
<p><code>thenable</code> 对象指的是具有 <code>then</code> 方法的对象，比如下面这个对象。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> thenable = &#123;</span><br><span class="line">  then: <span class="function"><span class="keyword">function</span>(<span class="params">resolve, reject</span>) </span>&#123;</span><br><span class="line">    resolve(<span class="number">42</span>);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p><code>Promise.resolve</code> 方法会将这个对象转为 <code>Promise</code> 对象，然后就立即执行 <code>thenable</code> 对象的 <code>then</code> 方法。</p>
<ul>
<li>参数不是具有 <code>then</code> 方法的对象，或根本就不是对象</li>
</ul>
<p>如果参数是一个原始值，或者是一个不具有 <code>then</code>  方法的对象，则 <code>Promise.resolve</code> 方法返回一个新的 <code>Promise</code> 对象，状态为 <code>resolved</code>。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> p = <span class="built_in">Promise</span>.resolve(<span class="string">'Hello'</span>);</span><br><span class="line"></span><br><span class="line">p.then(<span class="function"><span class="keyword">function</span> (<span class="params">s</span>)</span>&#123;</span><br><span class="line">  <span class="built_in">console</span>.log(s)</span><br><span class="line">&#125;);</span><br><span class="line"><span class="comment">// Hello</span></span><br></pre></td></tr></table></figure>

<ul>
<li>不带有任何参数</li>
</ul>
<p><code>Promise.resolve()</code> 方法允许调用时不带参数，直接返回一个 <code>resolved</code> 状态的 <code>Promise</code> 对象。</p>
<p>所以，如果希望得到一个 <code>Promise</code> 对象，比较方便的方法就是直接调用 <code>Promise.resolve()</code>  方法。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> p = <span class="built_in">Promise</span>.resolve();</span><br><span class="line"></span><br><span class="line">p.then(<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="comment">//...</span></span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure>

<h2 id="Promise-reject"><a href="#Promise-reject" class="headerlink" title="Promise.reject()"></a>Promise.reject()</h2><p><code>Promise.reject(reason)</code> 方法也会返回一个新的 <code>Promise</code> 实例，该实例的状态为 <code>rejected</code>。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> p = <span class="built_in">Promise</span>.reject(<span class="string">'出错了'</span>);</span><br><span class="line"><span class="comment">// 等同于</span></span><br><span class="line"><span class="keyword">const</span> p = <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function">(<span class="params">resolve, reject</span>) =&gt;</span> reject(<span class="string">'出错了'</span>))</span><br><span class="line"></span><br><span class="line">p.then(<span class="literal">null</span>, <span class="function"><span class="keyword">function</span> (<span class="params">s</span>) </span>&#123;</span><br><span class="line">  <span class="built_in">console</span>.log(s)</span><br><span class="line">&#125;);</span><br><span class="line"><span class="comment">// 出错了</span></span><br></pre></td></tr></table></figure>

<h1 id="Iterator-和-for-…-of-循环"><a href="#Iterator-和-for-…-of-循环" class="headerlink" title="Iterator 和 for … of 循环"></a>Iterator 和 for … of 循环</h1><h2 id="Iterator-概述"><a href="#Iterator-概述" class="headerlink" title="Iterator 概述"></a>Iterator 概述</h2><p>JavaScript 原有的表示“集合”的数据结构，主要是数组（<code>Array</code>）和对象（<code>Object</code>），ES6 又添加了 <code>Map</code> 和 <code>Set</code>。这样就有了四种数据集合，用户还可以组合使用它们，定义自己的数据结构，比如数组的成员是 <code>Map</code>，<code>Map</code> 的成员是对象。这样就需要一种统一的接口机制，来处理所有不同的数据结构。</p>
<p>遍历器（<code>Iterator</code>）就是这样一种机制。它是一种接口，为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 <code>Iterator</code> 接口，就可以完成遍历操作（即依次处理该数据结构的所有成员）。</p>
<p><code>Iterator</code> 的作用有三个：</p>
<ol>
<li>为各种数据结构，提供一个统一的、简便的访问接口。</li>
<li>使得数据结构的成员能够按某种次序排列。</li>
<li>ES6 创造了一种新的遍历命令 <code>for ... of</code> 循环，Iterator 接口主要供 <code>for ... of</code> 消费。</li>
</ol>
<h2 id="默认-Iterator-接口"><a href="#默认-Iterator-接口" class="headerlink" title="默认 Iterator 接口"></a>默认 Iterator 接口</h2><p><code>Iterator</code> 接口的目的，就是为所有数据结构，提供了一种统一的访问机制，即 <code>for ... of</code> 循环。当使用 <code>for ... of</code> 循环遍历某种数据结构时，该循环会自动去寻找 <code>Iterator</code> 接口。</p>
<p>ES6 的有些数据结构原生具备 <code>Iterator</code> 接口（比如数组），即不用任何处理，就可以被 <code>for ... of</code> 循环遍历。原因在于，这些数据结构原生部署了 <code>Symbol.iterator</code> 属性，另外一些数据结构没有（比如对象）。凡是部署了 <code>Symbol.iterator</code> 属性的数据结构，就称为部署了遍历器接口。调用这个接口，就会返回一个遍历器对象。</p>
<p>原生具备 <code>Iterator</code> 接口的数据结构如下：</p>
<ul>
<li><code>Array</code></li>
<li><code>Map</code></li>
<li><code>Set</code></li>
<li><code>String</code></li>
<li><code>TypedArray</code></li>
<li>函数的 <code>arguments</code> 对象</li>
<li><code>NodeList</code> 对象</li>
</ul>
<h2 id="for-…-of-循环"><a href="#for-…-of-循环" class="headerlink" title="for … of 循环"></a>for … of 循环</h2><p>ES6 引入了 <code>for ... of</code> 循环，作为遍历所有数据结构的统一的方法。</p>
<p>一个数据结构只要部署了 <code>Symbol.iterator</code> 属性，就被视为具有 <code>iterator</code>  接口，就可以用 <code>for...of</code> 循环遍历它的成员。也就是说，<code>for...of</code> 循环内部调用的是数据结构的 <code>Symbol.iterator</code> 方法。</p>
<p>==<code>for...of</code> 循环可以使用的范围包括 <code>数组、Set 和 Map 结构、某些类似数组的对象（比如 arguments 对象、DOM NodeList 对象）、后文的 Generator 对象，以及字符串</code>。==</p>
<h3 id="数组"><a href="#数组" class="headerlink" title="数组"></a>数组</h3><p>数组原生具备 <code>iterator</code> 接口（即默认部署了 <code>Symbol.iterator</code> 属性），<code>for...of</code> 循环本质上就是调用这个接口产生的遍历器，可以用下面的代码证明。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> arr = [<span class="string">'red'</span>, <span class="string">'green'</span>, <span class="string">'blue'</span>];</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">let</span> v <span class="keyword">of</span> arr) &#123;</span><br><span class="line">  <span class="built_in">console</span>.log(v); <span class="comment">// red green blue</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> obj = &#123;&#125;;</span><br><span class="line">obj[<span class="built_in">Symbol</span>.iterator] = arr[<span class="built_in">Symbol</span>.iterator].bind(arr);</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">let</span> v <span class="keyword">of</span> obj) &#123;</span><br><span class="line">  <span class="built_in">console</span>.log(v); <span class="comment">// red green blue</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>JavaScript 原有的 <code>for ... in</code> 循环，只能获得对象的键名，不能直接获取键值。ES6 提供 <code>for ... of</code> 循环，允许遍历获得键值。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> arr = [<span class="string">'a'</span>, <span class="string">'b'</span>, <span class="string">'c'</span>, <span class="string">'d'</span>];</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">let</span> a <span class="keyword">in</span> arr) &#123;</span><br><span class="line">  <span class="built_in">console</span>.log(a); <span class="comment">// 0 1 2 3</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">let</span> a <span class="keyword">of</span> arr) &#123;</span><br><span class="line">  <span class="built_in">console</span>.log(a); <span class="comment">// a b c d</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="Set-和-Map-结构"><a href="#Set-和-Map-结构" class="headerlink" title="Set 和 Map 结构"></a>Set 和 Map 结构</h3><p><code>Set</code> 和 <code>Map</code> 结构也原生具有 <code>Iterator</code> 接口，可以直接使用 <code>for ... of</code> 循环。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> engines = <span class="keyword">new</span> <span class="built_in">Set</span>([<span class="string">"Gecko"</span>, <span class="string">"Trident"</span>, <span class="string">"Webkit"</span>, <span class="string">"Webkit"</span>]);</span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">var</span> e <span class="keyword">of</span> engines) &#123;</span><br><span class="line">  <span class="built_in">console</span>.log(e);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// Gecko</span></span><br><span class="line"><span class="comment">// Trident</span></span><br><span class="line"><span class="comment">// Webkit</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> es6 = <span class="keyword">new</span> <span class="built_in">Map</span>();</span><br><span class="line">es6.set(<span class="string">"edition"</span>, <span class="number">6</span>);</span><br><span class="line">es6.set(<span class="string">"committee"</span>, <span class="string">"TC39"</span>);</span><br><span class="line">es6.set(<span class="string">"standard"</span>, <span class="string">"ECMA-262"</span>);</span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">var</span> [name, value] <span class="keyword">of</span> es6) &#123;</span><br><span class="line">  <span class="built_in">console</span>.log(name + <span class="string">": "</span> + value);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// edition: 6</span></span><br><span class="line"><span class="comment">// committee: TC39</span></span><br><span class="line"><span class="comment">// standard: ECMA-262</span></span><br></pre></td></tr></table></figure>

<p>遍历的顺序是按照各个成员被添加进数据结构的顺序。<code>Set</code> 结构遍历时，==返回的是一个值，== 而 <code>Map</code> 结构遍历时，==返回的是一个数组，== 该数组成员分别为当前  <code>Map</code> 成员的键名和键值。</p>
<h3 id="计算生成的数据结构"><a href="#计算生成的数据结构" class="headerlink" title="计算生成的数据结构"></a>计算生成的数据结构</h3><p>有些数据结构是在现有的数据结构的基础上，计算生成的。比如，<code>ES6 的数组</code>、<code>Set</code>、<code>Map</code>  都部署了以下三个方法，调用后都返回遍历器对象。</p>
<ul>
<li><code>entries()</code> 返回一个遍历器对象，用来遍历 <code>[键名，键名]</code> 组成的数组。对于数组，键名就是索引值；对于 <code>Set</code> ，键名与键值相同。<code>Map</code> 结构的 <code>Iterator</code> 接口，默认就是调用 <code>entries</code> 方法。</li>
<li><code>keys()</code> 返回一个遍历器对象，用来遍历所有的键名。</li>
<li><code>values()</code> 返回一个遍历器对象，用来遍历所有的键值。</li>
</ul>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> arr = [<span class="string">'a'</span>, <span class="string">'b'</span>, <span class="string">'c'</span>];</span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">let</span> pair <span class="keyword">of</span> arr.entries()) &#123;</span><br><span class="line">  <span class="built_in">console</span>.log(pair);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// [0, 'a']</span></span><br><span class="line"><span class="comment">// [1, 'b']</span></span><br><span class="line"><span class="comment">// [2, 'c']</span></span><br></pre></td></tr></table></figure>

<h3 id="类似数组的对象"><a href="#类似数组的对象" class="headerlink" title="类似数组的对象"></a>类似数组的对象</h3><p>类似数组的对象包括几类。下面是 <code>for ... of</code> 循环用于字符串、<code>DOM NodeList</code> 对象、<code>arguments</code> 对象的例子。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 字符串</span></span><br><span class="line"><span class="keyword">let</span> str = <span class="string">"hello"</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">let</span> s <span class="keyword">of</span> str) &#123;</span><br><span class="line">  <span class="built_in">console</span>.log(s); <span class="comment">// h e l l o</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// DOM NodeList对象</span></span><br><span class="line"><span class="keyword">let</span> paras = <span class="built_in">document</span>.querySelectorAll(<span class="string">"p"</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">let</span> p <span class="keyword">of</span> paras) &#123;</span><br><span class="line">  p.classList.add(<span class="string">"test"</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// arguments对象</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">printArgs</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">for</span> (<span class="keyword">let</span> x <span class="keyword">of</span> <span class="built_in">arguments</span>) &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(x);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line">printArgs(<span class="string">'a'</span>, <span class="string">'b'</span>);</span><br><span class="line"><span class="comment">// 'a'</span></span><br><span class="line"><span class="comment">// 'b'</span></span><br></pre></td></tr></table></figure>

<p>==不是所有的类似数组对象都具有 <code>Iterator</code>  接口，最简单的解决方法，就是使用 <code>Array.from</code> 方法将其转为数组。==</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> arrayLike = &#123; <span class="attr">length</span>: <span class="number">2</span>, <span class="number">0</span>: <span class="string">'a'</span>, <span class="number">1</span>: <span class="string">'b'</span> &#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 报错</span></span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">let</span> x <span class="keyword">of</span> arrayLike) &#123;</span><br><span class="line">  <span class="built_in">console</span>.log(x);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 正确</span></span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">let</span> x <span class="keyword">of</span> <span class="built_in">Array</span>.from(arrayLike)) &#123;</span><br><span class="line">  <span class="built_in">console</span>.log(x);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="对象"><a href="#对象" class="headerlink" title="对象"></a>对象</h3><p>对于普通的对象，<code>for...of</code> 结构不能直接使用，会报错，必须部署了 <code>Iterator</code> 接口后才能使用。但是，这样情况下，<code>for...in</code> 循环依然可以用来遍历键名。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> es6 = &#123;</span><br><span class="line">  edition: <span class="number">6</span>,</span><br><span class="line">  committee: <span class="string">"TC39"</span>,</span><br><span class="line">  standard: <span class="string">"ECMA-262"</span></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">let</span> e <span class="keyword">in</span> es6) &#123;</span><br><span class="line">  <span class="built_in">console</span>.log(e);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// edition</span></span><br><span class="line"><span class="comment">// committee</span></span><br><span class="line"><span class="comment">// standard</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">let</span> e <span class="keyword">of</span> es6) &#123;</span><br><span class="line">  <span class="built_in">console</span>.log(e);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// TypeError: es6[Symbol.iterator] is not a function</span></span><br></pre></td></tr></table></figure>

<p>一种解决方法是，使用 <code>Object.keys</code> 方法将对象的键名生成一个数组，然后遍历这个数组。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span> (<span class="keyword">var</span> key <span class="keyword">of</span> <span class="built_in">Object</span>.keys(someObject)) &#123;</span><br><span class="line">  <span class="built_in">console</span>.log(key + <span class="string">': '</span> + someObject[key]);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>另一个方法是使用 <code>Generator</code> 函数将对象重新包装一下。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span>* <span class="title">entries</span>(<span class="params">obj</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">for</span> (<span class="keyword">let</span> key <span class="keyword">of</span> <span class="built_in">Object</span>.keys(obj)) &#123;</span><br><span class="line">    <span class="keyword">yield</span> [key, obj[key]];</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">let</span> [key, value] <span class="keyword">of</span> entries(obj)) &#123;</span><br><span class="line">  <span class="built_in">console</span>.log(key, <span class="string">'-&gt;'</span>, value);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// a -&gt; 1</span></span><br><span class="line"><span class="comment">// b -&gt; 2</span></span><br><span class="line"><span class="comment">// c -&gt; 3</span></span><br></pre></td></tr></table></figure>

<h3 id="与其他遍历语法的比较"><a href="#与其他遍历语法的比较" class="headerlink" title="与其他遍历语法的比较"></a>与其他遍历语法的比较</h3><h4 id="以数组为例"><a href="#以数组为例" class="headerlink" title="以数组为例"></a>以数组为例</h4><ul>
<li><code>for</code> 循环比较原始，写法比较麻烦。</li>
<li><code>forEach</code> 问题在于无法中途跳出 <code>forEach</code> 循环，<code>break</code> 命令或 <code>return</code> 命令都不能奏效。</li>
</ul>
<h4 id="for-in-循环有几个缺点"><a href="#for-in-循环有几个缺点" class="headerlink" title="for ... in 循环有几个缺点"></a><code>for ... in</code> 循环有几个缺点</h4><ul>
<li>数组的键名是数字，但是 <code>for...in</code> 循环是以字符串作为键名 <code>“0”、“1”、“2”</code> 等等。</li>
<li><code>for...in</code> 循环不仅遍历数字键名，还会遍历手动添加的其他键，甚至包括原型链上的键。</li>
<li>某些情况下，<code>for...in</code> 循环会以任意顺序遍历键名。</li>
</ul>
<h4 id="for-of-的优势"><a href="#for-of-的优势" class="headerlink" title="for ... of 的优势"></a><code>for ... of</code> 的优势</h4><ul>
<li>有着同 <code>for ... in</code> 一样的简洁语法，但是没有 <code>for ... in</code> 那些缺点。</li>
<li>不同于 <code>forEach</code> 方法，它可以与 <code>break、continue 和 return</code> 配合使用。</li>
<li>提供了遍历所有数据结构的统一操作接口。</li>
</ul>
<h1 id="Class-基本用法"><a href="#Class-基本用法" class="headerlink" title="Class 基本用法"></a>Class 基本用法</h1><h2 id="简介-2"><a href="#简介-2" class="headerlink" title="简介"></a>简介</h2><h3 id="类的由来"><a href="#类的由来" class="headerlink" title="类的由来"></a>类的由来</h3><p>==JavaScript 语言中，生成实例对象的传统方法是通过构造函数。==</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Point</span>(<span class="params">x, y</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">this</span>.x = x;</span><br><span class="line">  <span class="keyword">this</span>.y = y;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">Point.prototype.toString = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="string">'('</span> + <span class="keyword">this</span>.x + <span class="string">', '</span> + <span class="keyword">this</span>.y + <span class="string">')'</span>;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> p = <span class="keyword">new</span> Point(<span class="number">1</span>, <span class="number">2</span>);</span><br></pre></td></tr></table></figure>

<p>上面这种写法跟传统的面向对象语言（比如 <code>C++</code> 和 <code>Java</code>）差异很大，很容易让新学习这门语言的程序员感到困惑。</p>
<p>ES6 提供了更接近传统语言的写法，引入了 <code>Class</code>（类）这个概念，作为对象的模板。通过 <code>class</code> 关键字，可以定义类。</p>
<p>基本上，==ES6 的 <code>class</code> 可以看作只是一个语法糖，它的绝大部分功能，ES5 都可以做到，新的 <code>class</code> 写法只是让对象原型的写法更加清晰、更像面向对象编程的语法而已。== 上面的代码用 ES6 的 <code>class</code> 改写，就是下面这样。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Point</span> </span>&#123;</span><br><span class="line">  <span class="keyword">constructor</span>(x, y) &#123;</span><br><span class="line">    <span class="keyword">this</span>.x = x;</span><br><span class="line">    <span class="keyword">this</span>.y = y;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  toString() &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="string">'('</span> + <span class="keyword">this</span>.x + <span class="string">', '</span> + <span class="keyword">this</span>.y + <span class="string">')'</span>;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>上面代码定义了一个“类”，可以看到里面有一个 <code>constructor</code> 方法，这就是构造方法，而 <code>this</code> 关键字则代表实例对象。也就是说，==ES5 的构造函数 <code>Point</code>，对应 ES6 的 <code>Point</code> 类的构造方法。==</p>
<p><code>Point</code> 类除了构造方法，还定义了一个 <code>toString</code> 方法。注意，==定义“类”的方法的时候，前面不需要加上 <code>function</code> 这个关键字，直接把函数定义放进去了就可以了。另外，方法之间不需要逗号分隔，加了会报错。==</p>
<p>ES6 的类，完全可以看作构造函数的另一种写法。类的数据类型就是函数，类本身就指向构造函数。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Point</span> </span>&#123;</span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">typeof</span> Point <span class="comment">// "function"</span></span><br><span class="line">Point === Point.prototype.constructor <span class="comment">// true</span></span><br></pre></td></tr></table></figure>

<p>使用的时候，也是直接对类使用 <code>new</code>  命令，跟构造函数的用法完全一致。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Bar</span> </span>&#123;</span><br><span class="line">  doStuff() &#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">'stuff'</span>);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> b = <span class="keyword">new</span> Bar();</span><br><span class="line">b.doStuff() <span class="comment">// "stuff"</span></span><br></pre></td></tr></table></figure>

<p>构造函数的 prototype 属性，在 ES6 的“类”上面继续存在。事实上，类的所有方法都定义在类的 prototype 属性上面。</p>
<p>class Point {<br>    constructor(){<br>        // …<br>    }</p>
<pre><code>toString(){
    // ...
}

toValue(){
    // ...
}</code></pre><p>}</p>
<p>// 等同于</p>
<p>Point.prototype = {<br>    constructor(){},<br>    toString(){},<br>    toValue(){},<br>};</p>
<p>在类的实例上面调用方法，其实就是调用原型上的方法。</p>
<p>class B {}<br>let b = new B();</p>
<p>b.constructor = B.prototype.constructor // true</p>
<p>由于类的方法都定义在 prototype 对象上面，所以类的新方法可以添加在 prototype 对象上面。Object.assign 方法可以很方便地一次向类添加多个方法。</p>
<p>class Point {<br>    constructor(){<br>        // …<br>    }<br>}</p>
<p>Object.assign(Point.prototype,{<br>    toString(){},<br>    toValue(){}<br>})</p>
<p>prototype 对象的 constructor 属性，直接指向“类”的本身，这与 ES5 的行为是一致的。</p>
<p>Point.prototype.constructor === Point // true</p>
<p>类的内部所有定义的方法，都是不可枚举的。</p>
<p>class Point {<br>    constructor(x,y){<br>        // …<br>    }</p>
<pre><code>toString(){
    // ...
}</code></pre><p>}</p>
<p>Object.keys(Point.prototype) // []<br>Object.getOwnPrototypeNames(Point.prototype) // [‘constructor’,’toString’]</p>
<p>constructor 方法</p>
<p>constructor 方法是类的默认方法，通过 new 命令生成对象实例时，自动调用该方法。一个类必须有 constructor 方法，如果没有显示定义，一个空的 constructor 方法会被默认添加。</p>
<p>constructor 方法默认返回实例对象（即 this），完全可以指定返回另一个对象。</p>
<p>class Foo {<br>    constructor(){<br>        retrun Object.create(null);<br>    }<br>}</p>
<p>new Foo() instanceof Foo<br>// false</p>
<p>上面代码中，constructor 函数返回一个全新的对象，结果导致实例对象不是 Foo 类的实例。</p>
<p>类必须使用 new 调用，否则会报错。这是它跟普通构造函数的一个主要区别，后者不用 new 也可以执行。</p>
<p>类的实例</p>
<p>生成类的实例写法，与 ES5 完全一样，也是使用 new 命令。</p>
<p>class Point {<br>    // …<br>}</p>
<p>// 报错<br>var point = Point(2,3);</p>
<p>// 正确<br>var point = new Point(2,3);</p>
<p>与 ES5 一样，实例的属性除非显示定义在其本身（即定义在 this 对象上），否则都是定义在原型上（即定义在 class 上）。</p>
<p>// 定义类<br>class Point {<br>    constructor(x,y){<br>        this.x = x;<br>        this.y = y;<br>    }</p>
<pre><code>toString(){
    return &apos;(&apos; + this.x + &apos;, &apos; + this.y + &apos;)&apos;;
}</code></pre><p>}</p>
<p>var point = new Point(2,3);</p>
<p>point.toString() // (2,3)</p>
<p>point.hasOwnProperty(‘x’) // true<br>point.hasOwnProperty(‘y’) // true<br>point.hasOwnProperty(‘toString’) // false<br>point.<strong>proto</strong>.hasOwnProperty(‘toString’) // true</p>
<p>与 ES5 一样，类的所有实例共享一个原型对象。</p>
<p>var p1 = new Point(2,3);<br>var p2 = new Point(3,2);</p>
<p>p1.<strong>proto</strong> === p2.<strong>proto</strong> // true</p>
<p>使用实例的 <strong>proto</strong> 属性改写原型，必须相当谨慎，不推荐使用，因为这会改变“类”的原始定义，影响到所有实例。</p>
<p>取值函数（getter）和存值函数（setter）</p>
<p>与 ES5 一样，在“类”的内部可以使用 get 和 set 关键字，对某个属性设置存值函数和取值函数，拦截该属性的存取行为。</p>
<p>class MyClass {<br>    constructor(){<br>        // …<br>    }</p>
<pre><code>get prop(){
    return &apos;getter&apos;;
}

set prop(value){
    console.log(&apos;setter:&apos; + value);
}</code></pre><p>}</p>
<p>let inst = new MyClass();</p>
<p>inst.prop = 123; // setter:123;</p>
<p>inst.prop // ‘getter’</p>
<p>属性表达式</p>
<p>类的属性名，可以采用表达式。</p>
<p>let methodName = ‘getArea’;</p>
<p>class Square {<br>  constructor(length) {<br>    // …<br>  }</p>
<p>  <a href="">methodName</a> {<br>    // …<br>  }<br>}</p>
<p>Class 表达式</p>
<p>类可以使用表达式的形式定义。</p>
<p>const MyClass = class Me {<br>    getClassName(){<br>        retrun Me.name;<br>    }<br>}</p>
<p>上面代码使用类定义了一个类。需要注意的是，这个类的名字是 Me，但是 Me 只在 Class 的内部可用，指代当前类。在 Class 外部，这个类只能用 MyClass 引用。</p>
<p>let inst = new MyClass();<br>inst.getClassName() // Me<br>Me.name // ReferanceError: Me is not defined</p>
<p>如果类的内部没有用到的话，可以省略 Me，也就是可以写成下面的形式。</p>
<p>const MyClass = class { /* … */ };</p>
<p>注意点</p>
<p>严格模式</p>
<p>类和模块的内部，默认就是严格模式，所以不需要使用 use strict 指定运行模式。</p>
<p>不存在提升</p>
<p>类不存在变量提升，这一点与 ES5 完全不同。</p>
<p>new Foo(); // ReferenceError<br>class Foo {}</p>
<p>上述代码中，Foo 类使用在前，定义在后，这样会报错，因为 ES6 不会把类的声明提升到代码的头部。这种规定原因是与类的继承有关，必须保证子类在父类之后定义。</p>
<p>name 属性</p>
<p>class Point {}<br>Point.name // ‘Point’</p>
<p>name 属性总是返回紧跟在 class 关键字后面的类名。</p>
<p>Generator 方法</p>
<p>如果在某个方法之前加上星号（*），就表示该方法是一个 Generator 函数。</p>
<p>class Foo {<br>    constructor(…args){<br>        this.args = args;<br>    }<br>    *<a href="">Symbol.iterator</a>{<br>        for(let arg of this.args){<br>            yield arg;<br>        }<br>    }<br>}</p>
<p>for(let x of new Foo(‘hello’,’world’)){<br>    console.log(x);<br>}<br>// hello<br>// world</p>
<p>上述代码，Foo 类的 Symbol.iterator 方法前有一个星号，表示该方法是一个 Generator 函数。Symbol.iterator 方法返回一个 Foo 类的默认遍历器，for … of 循环会自动调用这个遍历器。</p>
<p>this 的指向</p>
<p>类的方法内部如果含有 this ，它默认指向类的实例。但是，必须非常小心，一旦单独使用该方法，很可能报错。</p>
<p>class Logger {<br>    printName(name = ‘there’){<br>        this.print(<code>Hello ${name}</code>);<br>    }</p>
<pre><code>print(text){
    console.log(text);
}</code></pre><p>}</p>
<p>const logger = new Logger();<br>const { printName } = logger;<br>printName(); // TypeError:Cannot read property ‘print’ of undefined</p>
<p>上述代码中，printName 方法中的 this，默认指向 Logger 类的实例。但是，如果将这个方法提取出来单独使用，this 就会指向该方法运行时所在的环境，从而导致找不到 print 方法而报错。</p>
<p>可以在构造方法中绑定 this 来解决该问题。</p>
<p>class Logger {<br>    constructor(){<br>        this.printName = this.printName.bind(this)<br>    }<br>    // …<br>}</p>
<p>或者使用箭头函数。</p>
<p>class Obj {<br>    constructor(){<br>        this.getThis = () =&gt; this;<br>    }<br>}</p>
<p>const myObj = new Obj();<br>myObj.getThis() === myObj // true</p>
<p>静态方法</p>
<p>类相当于实例的原型，所有在类中定义的方法，都会被实例继承。如果在一个方法前，加上 static 关键字，就表示该方法不会被实例继承，而是直接通过类来调用，这就称为“静态方法”。</p>
<p>class Foo {<br>    static classMethod(){<br>        return ‘hello’;<br>    }<br>}</p>
<p>Foo.classMethod(){} // ‘hello’<br>var foo = new Foo();<br>foo.classMethod() // TypeError:foo.classMethod is not a function</p>
<p>注意，如果一个静态方法包含 this 关键字，这个 this 指的是类，而不是实例。</p>
<p>class Foo {<br>    static bar(){<br>        this.bar();<br>    }</p>
<pre><code>static baz(){
    console.log(&apos;hello&apos;);
}

baz(){
    console.log(&apos;world&apos;);
}</code></pre><p>}</p>
<p>上面代码中，静态方法 bar 调用了 this.baz ，这里的 this 指的是 Foo 类，而不是 Foo 的实例，等同于调用 Foo.baz。另外，从这个例子可以看出，静态方法可以与非静态方法重名。</p>
<p>父类的静态方法，可以被子类继承。</p>
<p>class Foo {<br>    static classMethod(){<br>        retrun ‘hello’;<br>    }<br>}</p>
<p>class Bar extends Foo {</p>
<p>}</p>
<p>Bar.classMethod() // ‘hello’</p>
<p>静态方法也可以从 super 对象上调用。</p>
<p>class Foo {<br>    static classMethod(){<br>        retrun ‘hello’;<br>    }<br>}</p>
<p>class Bar extends Foo {<br>    static classMethod(){<br>        return super.classMethod() + ‘,too’;<br>    }<br>}</p>
<p>Bar.classMethod() // ‘hello,too’</p>
<p>实例属性的新写法</p>
<p>实例属性除了定义在 constructor（）方法里面的 this 上面，也可以定义在类的最顶层。</p>
<p>class IncreasingCounter {<br>    constructor(){<br>        this._count = 0;<br>    }<br>    get value(){<br>        console.log(‘Getting the current vlaue!’);<br>        return this._count;<br>    }<br>    increment(){<br>        this._count++;<br>    }<br>}</p>
<p>上面代码中，实例属性 this._count 定义在 constructor（）方法里面。另一种写法是，这个属性也可以定义在类的最顶层，其他都不变。</p>
<p>class IncreasingCounter {<br>    _count = 0;<br>    get value() {<br>    console.log(‘Getting the current value!’);<br>    return this._count;<br>  }<br>   increment() {<br>    this._count++;<br>  }<br>}</p>
<p>这种写法好处是，所有实例对象自身的属性都定义在类的头部，看上去比较整齐，一眼就可以看出这个类有哪些实例属性。</p>
<p>class foo {<br>    bar = ‘hello’;<br>    baz = ‘world’;</p>
<pre><code>constructor(){
    // ...
}</code></pre><p>}</p>
<p>静态属性</p>
<p>静态属性指的是 Class 本身的属性，即 Class.propName，而不是定义在实例对象（this）上的属性。</p>
<p>class Foo { }</p>
<p>Foo.prop = 1;<br>Foo.prop // 1</p>
<p>上面的写法为 Foo 类定义了一个静态属性 prop。</p>
<p>目前，只有这种写法可行，因为 ES6 明确规定，Class 内部只有静态方法，没有静态属性。现有一个提案提供了类的静态属性，写法是在实例属性的前面，加上 static 关键字。</p>
<p>class MyClass {<br>    static myStaticProp = 42;</p>
<pre><code>constructor(){
    console.log(MyClass.myStaticProp); // 42
}</code></pre><p>}</p>
<p>私有方法和私有属性</p>
<p>现有方案</p>
<p>命名加以区分，比如 bar 和 _bar；或者利用 Symbol 值的唯一性，将私有方法的名字命名为一个 Symbol 值。</p>
<p>私有属性提案</p>
<p>目前，有个提案，为 class 加了私有属性。方法是在属性名之前，使用 # 表示。 </p>
<p>new.target 属性</p>
<p>new 是从构造函数生成实例对象的命令。ES6 为 new 命令引入了一个 new.target 属性，该属性一般用在构造函数之中，返回 new 命令作用于的那个构造函数。如果构造函数不是通过 new 命令或 Reflect.constructor（）调用的，new.target 会返回 undefined，因此这个属性可以用来确定构造函数是怎么调用的。</p>
<p>function Person(name) {<br>  if (new.target !== undefined) {<br>    this.name = name;<br>  } else {<br>    throw new Error(‘必须使用 new 命令生成实例’);<br>  }<br>}</p>
<p>// 另一种写法<br>function Person(name) {<br>  if (new.target === Person) {<br>    this.name = name;<br>  } else {<br>    throw new Error(‘必须使用 new 命令生成实例’);<br>  }<br>}</p>
<p>var person = new Person(‘张三’); // 正确<br>var notAPerson = Person.call(person, ‘张三’);  // 报错</p>
<h1 id="Module-用法"><a href="#Module-用法" class="headerlink" title="Module 用法"></a>Module 用法</h1><h2 id="概述-3"><a href="#概述-3" class="headerlink" title="概述"></a>概述</h2><p>历史上，JavaScript 一直没有模块体系，给大型应用开发造成不便。</p>
<p>==在 ES6 之前，社区制定了一些模块加载方案，主要有 <code>CommonJS</code> 和 <code>AMD</code> 两种。<code>CommonJS</code> 用于服务器，<code>AMD</code> 用于浏览器。==</p>
<p>ES6 在语言标准层面上，实现了模块功能。==ES6 模块的设计思想是尽量的静态话，使得编译时就能确定模块化的依赖关系，以及输入和输出的变量。ES6 可以在编译时就完成模块加载，效率要比 <code>CommonJS</code> 模块的加载方式高。当然，这样也导致了没法引用 ES6 模块本身，因为它不是对象。==</p>
<p>除了静态加载带来的各种好处，ES6 模块还有以下好处。</p>
<ul>
<li>不再需要 <code>UMD</code> 模块格式了，将来服务器和浏览器都会支持 ES6 模块格式。目前，通过各种工具库，已经做到了这一点。</li>
<li>将来浏览器的新 <code>API</code> 就能用模块格式提供，不再必须做成全局变量或者 <code>navigator</code> 对象的属性。</li>
<li>不再需要对象作为命名空间（比如 <code>Math</code> 对象），未来这些功能可以通过模块提供。</li>
</ul>
<h2 id="严格模式"><a href="#严格模式" class="headerlink" title="严格模式"></a>严格模式</h2><p>ES6 的模块采用自动严格模式，不管你有没有在模块头部加 <code>“use strict”</code>。</p>
<p>严格模式主要有以下限制：</p>
<ul>
<li>变量必须声明后再使用</li>
<li>函数的参数不能有同名属性，否则报错</li>
<li>不能使用 <code>with</code> 语句</li>
<li>不能对只读属性赋值，否则报错</li>
<li>不能使用前缀 <code>0</code> 表示八进制数，否则报错</li>
<li>不能删除不可删除的属性，否则报错</li>
<li>不能删除变量 <code>delete prop</code>，会报错，只能删除属性 <code>delete global[prop]</code></li>
<li><code>eval</code> 不会在它的外层作用域引入变量</li>
<li><code>eval</code> 和 <code>arguments</code> 不能被重新赋值</li>
<li><code>arguments</code> 不会自动反映函数参数的变化</li>
<li>不能使用 <code>arguments.callee</code></li>
<li>不能使用 <code>arguments.caller</code></li>
<li>禁止 <code>this</code> 指向全局对象</li>
<li>不能使用 <code>fn.caller</code> 和 <code>fn.arguments</code> 获取函数调用的堆栈</li>
<li>增加了保留字（比如 <code>protected、static</code> 和 <code>interface</code>）</li>
</ul>
<h2 id="export-命令"><a href="#export-命令" class="headerlink" title="export 命令"></a>export 命令</h2><p>==模块功能主要有两个命令构成：<code>export</code> 和 <code>import</code> 。==</p>
<p>==<code>export</code> 命令用于规定模块的对外接口，<code>import</code> 命令用于输入其他模块提供的功能。==</p>
<p>==一个模块就是一个独立的文件。该文件内部的所有变量，外部无法获取。如果希望外部能够读取模块内部的的某个变量，就必须使用 <code>export</code> 关键字输出该变量。==</p>
<p>下面是一个 JS 文件，里面使用 <code>export</code> 命令输出变量。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// profiles.js</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">var</span> firstName = <span class="string">'Michael'</span>;</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">var</span> lastName = <span class="string">'Jackson'</span>;</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">var</span> year = <span class="number">1958</span>;</span><br></pre></td></tr></table></figure>

<p>上面代码是 <em>profiles.js</em> 文件，保存了用户信息。ES6 将其视为一个模块，里面用 <code>export</code> 命令对外输出了三个变量。</p>
<p><code>export</code> 的写法，除了像上面这样，还有另外一种。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// profiles.js</span></span><br><span class="line"><span class="keyword">var</span> firstName = <span class="string">'Michael'</span>;</span><br><span class="line"><span class="keyword">var</span> lastName = <span class="string">'Jackson'</span>;</span><br><span class="line"><span class="keyword">var</span> year = <span class="number">1958</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> &#123; firstName, lastName, year &#125;;</span><br></pre></td></tr></table></figure>

<p>这种写法与前一种写法（直接放在 var 语句前）是等价的，但是应该==优先考虑使用这种写法。因为这样就可以在脚本尾部，一眼看清楚输出了哪些变量。==</p>
<p><code>export</code> 命令除了输出变量，还可以输出函数或类（<code>class</code>）。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="function"><span class="keyword">function</span> <span class="title">multiply</span>(<span class="params">x,y</span>)</span>&#123;</span><br><span class="line">    retrun x * y;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>通常情况下，<code>export</code> 输出的变量就是本来的名字，但是可以使用 <code>as</code> 关键字重命名。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">v1</span>(<span class="params"></span>)</span>&#123;...&#125;</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">v2</span>(<span class="params"></span>)</span>&#123;...&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> &#123;</span><br><span class="line">    v1 <span class="keyword">as</span> streamV1,</span><br><span class="line">    v2 <span class="keyword">as</span> streamV2,</span><br><span class="line">    v2 <span class="keyword">as</span> streamLatestVersion</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p>上面代码使用 <code>as</code> 关键字，重命名了函数 <code>v1</code> 和 <code>v2</code> 的对外接口。重命名后，<code>v2</code> 可以用不同的名字输出两次。</p>
<p>==<code>export</code> 命令规定的是对外的接口，必须与模块内部的变量建立一一对应关系。==</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 报错</span></span><br><span class="line"><span class="keyword">export</span> <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 报错</span></span><br><span class="line"><span class="keyword">var</span> m = <span class="number">1</span>;</span><br><span class="line"><span class="keyword">export</span> m;</span><br></pre></td></tr></table></figure>

<p>上面两种写法都会报错，因为没有提供对外的接口。第一种写法直接输出 <code>1</code>，第二种写法通过变量 <code>m</code>，还是直接输出 <code>1</code> 。<code>1</code> 只是一个值，不是接口。正确的写法是下面这样。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 写法一</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">var</span> m = <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 写法二</span></span><br><span class="line"><span class="keyword">var</span> m = <span class="number">1</span>;</span><br><span class="line"><span class="keyword">export</span> &#123; m &#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 写法三</span></span><br><span class="line"><span class="keyword">var</span> n = <span class="number">1</span>;</span><br><span class="line"><span class="keyword">export</span> &#123; n <span class="keyword">as</span> m &#125;;</span><br></pre></td></tr></table></figure>

<p>上面三种写法都是正确的，规定了对外的接口 <code>m</code> 。其他脚本可以通过这个接口，取到值 <code>1</code> 。它们的实质是，在接口名与模块内部变量之间，建立了一一对应的关系。</p>
<p>同样的，<code>function</code> 和 <code>class</code> 的输出，也必须遵循这样的写法。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 报错</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">f</span>(<span class="params"></span>)</span>&#123;...&#125;</span><br><span class="line"><span class="keyword">export</span> f;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 正确</span></span><br><span class="line"><span class="keyword">export</span> <span class="function"><span class="keyword">function</span> <span class="title">f</span>(<span class="params"></span>)</span>&#123;...&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 正确</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">f</span>(<span class="params"></span>)</span>&#123;&#125;</span><br><span class="line"><span class="keyword">export</span> &#123; f &#125;;</span><br></pre></td></tr></table></figure>

<p>==<code>export</code> 命令可以出现在模块的任何位置，只要处于模块顶层就可以。如果处于块级作用域内，就会报错。包括 <code>import</code> 命令也是如此。==</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="keyword">export</span> <span class="keyword">default</span> <span class="string">'bar'</span> <span class="comment">// SyntaxError</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h2 id="import-命令"><a href="#import-命令" class="headerlink" title="import 命令"></a>import 命令</h2><p>使用 <code>export</code> 命令定义了模块的对外接口以后，其他 JS 文件就可以通过 <code>import</code> 命令加载这个模块。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// main.js</span></span><br><span class="line"><span class="keyword">import</span> &#123; firstName, lastName, year &#125; <span class="keyword">from</span> <span class="string">'./profile.js'</span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">setName</span>(<span class="params">element</span>) </span>&#123;</span><br><span class="line">    element.textContent = firstName + <span class="string">''</span> + lastName;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><code>import</code> 命令接受一对大括号，里面指定要从其他模块导入的变量名。==大括号里面的变量名，必须与被导入模块（<em>profile.js</em>）对外接口的名称相同。==</p>
<p>如果想为输入的变量重新取一个名字，<code>import</code> 命令要使用 <code>as</code> 关键字，将输入的变量重命名。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; lastName <span class="keyword">as</span> surname &#125; <span class="keyword">from</span> <span class="string">'./profiles.js'</span></span><br></pre></td></tr></table></figure>

<p><code>import</code> 命令输入的变量都是只读的，因为它的本质是输入接口。也就是说，==不允许在加载模块的脚本里面，改写接口。==</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; a &#125; <span class="keyword">from</span> <span class="string">'./xxx.js'</span></span><br><span class="line"></span><br><span class="line">a = &#123;&#125;; <span class="comment">// Syntax Error:'a' is read-only;</span></span><br></pre></td></tr></table></figure>

<p>上述代码，脚本加载了变量 <code>a</code> ，对其赋值就会报错。但是，如果 <code>a</code> 是一个对象，改写 <code>a</code> 的属性是允许的。不过，这种写法很难查错，建议凡事输入的变量，都当作完全只读，不要轻易改变它的属性。</p>
<p>==<code>import</code> 后面的 <code>from</code> 指定模块文件的位置，可以是相对路径，也可以是绝对路径，<code>.js</code> 后缀可以省略。如果只是模块名，不带有路径，那么必须有配置文件，告诉 JavaScript 引擎该模块的位置。==</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> &#123; myMethod &#125; <span class="keyword">from</span> <span class="string">'util'</span>;</span><br></pre></td></tr></table></figure>

<p>==<code>import</code> 命令具有提升效果，会提升到整个模块的头部，首先执行。==</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">foo();</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> &#123; foo &#125; <span class="keyword">from</span> <span class="string">'my_module'</span>;</span><br></pre></td></tr></table></figure>

<p>上面的代码不会报错，因为 <code>import</code> 的执行早于 <code>foo</code> 的调用。这种行为的本质是，<code>import</code> 命令是编译阶段执行的，在代码运行之前。</p>
<p>==由于 <code>import</code> 是静态执行，所以不能使用表达式和变量，这些只有在运行时才能得到结果的语法结构。==</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 报错</span></span><br><span class="line"><span class="keyword">import</span> &#123;<span class="string">'f'</span>+<span class="string">'oo'</span>&#125; <span class="keyword">from</span> <span class="string">'my_module'</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 报错</span></span><br><span class="line"><span class="keyword">let</span> <span class="built_in">module</span> = <span class="string">'my_module'</span>;</span><br><span class="line"><span class="keyword">import</span> &#123; foo &#125; <span class="keyword">from</span> <span class="built_in">module</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 报错</span></span><br><span class="line"><span class="keyword">if</span>(x === <span class="number">1</span>)&#123;</span><br><span class="line">    <span class="keyword">import</span> &#123; foo &#125; <span class="keyword">from</span> <span class="string">'module1'</span>;</span><br><span class="line">&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    <span class="keyword">import</span> &#123; foo &#125; <span class="keyword">from</span> <span class="string">'module2'</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>上述都会报错，因为它们用到了表达式、变量和 <code>if</code>  语句。在静态分析阶段，这些语法都是没法得到值的。</p>
<p><code>import</code> 语句会执行所加载的模块，因此可以有下面的写法。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> <span class="string">'lodash'</span>;</span><br></pre></td></tr></table></figure>

<p>上面代码仅仅执行 <code>lodash</code> 模块，但不输入任何值。</p>
<p>==如果多次重复执行同一句 <code>import</code> 语句，那么只会执行一次，而不会执行多次。==</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> <span class="string">'lodash'</span>;</span><br><span class="line"><span class="keyword">import</span> <span class="string">'lodash'</span>;</span><br></pre></td></tr></table></figure>

<p>==目前阶段，通过 <code>Babel</code> 转码，CommonJS 模块的 <code>require</code> 命令和 ES6 模块的 <code>import</code> 命令，可以写在同一个模块里面，但是最好不要这样做。因为 <code>import</code> 在静态解析阶段执行，所以它是一个模块之中最早执行的。==</p>
<h2 id="模块的整体加载"><a href="#模块的整体加载" class="headerlink" title="模块的整体加载"></a>模块的整体加载</h2><p>整体加载，即用星号 <code>*</code> 指定一个对象，所有输出值都加载在这个对象上面。</p>
<p>下面是一个 <code>circle.js</code> 文件，它输出两个方法 <code>area</code> 和 <code>circumference</code>。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// circle.js</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="function"><span class="keyword">function</span> <span class="title">area</span>(<span class="params">radius</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="built_in">Math</span>.PI * radius * radius;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="function"><span class="keyword">function</span> <span class="title">circumference</span>(<span class="params">radius</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> <span class="number">2</span> * <span class="built_in">Math</span>.PI * radius;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>现在，加载这个模块。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">&#x2F;&#x2F; main.js</span><br><span class="line"></span><br><span class="line">import &#123; area, circumference &#125; from &#39;.&#x2F;circle&#39;;</span><br><span class="line"></span><br><span class="line">console.log(&#39;圆面积：&#39; + area(4));</span><br><span class="line">console.log(&#39;圆周长：&#39; + circumference(14));</span><br></pre></td></tr></table></figure>

<p>上面写法是逐一指定要加载的方法，整体加载的写法如下。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> * <span class="keyword">as</span> circle <span class="keyword">from</span> <span class="string">'./circle'</span>;</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(<span class="string">'圆面积：'</span> + circle.area(<span class="number">4</span>));</span><br><span class="line"><span class="built_in">console</span>.log(<span class="string">'圆周长：'</span> + circle.circumference(<span class="number">14</span>));</span><br></pre></td></tr></table></figure>

<p>注意，模块整体加载所在的那个对象（上例是 <code>circle</code>），应该是可以静态分析的，所以不允许运行时改变。下面的写法都是不允许的。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> * <span class="keyword">as</span> circle <span class="keyword">from</span> <span class="string">'./circle'</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 下面两行都是不允许的</span></span><br><span class="line">circle.foo = <span class="string">'hello'</span>;</span><br><span class="line">circle.area = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;&#125;;</span><br></pre></td></tr></table></figure>

<h2 id="export-default-命令"><a href="#export-default-命令" class="headerlink" title="export default 命令"></a>export default 命令</h2><p>前面例子中，使用 <code>import</code> 命令的时候，用户需要知道所要加载的变量名或函数名，否则无法加载。为了给用户提供方便，==可以不清楚模块名称就引入模块，就要用到 <code>export default</code> 命令，为模块指定默认输出。==</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// export-default.js</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="function"><span class="keyword">function</span> (<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">'foo'</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>上面代码是一个模块文件 <em>export-default.js</em> 它的默认输出是一个函数。</p>
<p>其他模块加载该模块时，<code>import</code> 命令可以为该匿名函数指定任意名字。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// import-default.js</span></span><br><span class="line"><span class="keyword">import</span> customName <span class="keyword">from</span> <span class="string">'./export-default'</span>;</span><br><span class="line">customName(); <span class="comment">// 'foo'</span></span><br></pre></td></tr></table></figure>

<p>上面代码的 <code>import</code> 命令，可以用任意名称指向 <em>export-default.js</em>  输出的方法，这时就不需要知道原模块输出的函数名。要注意，==这时 <code>import</code> 命令后面，不使用大括号。==</p>
<p><code>export default</code> 命令也可以用在非匿名函数前。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// export-default.js</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="function"><span class="keyword">function</span> <span class="title">foo</span> (<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">'foo'</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 或者写成</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">foo</span> (<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="string">'foo'</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> foo;</span><br></pre></td></tr></table></figure>

<p>上述代码中，<code>foo</code> 函数的函数名 <code>foo</code>，在模块外部都是无效的。加载的时候，视同匿名函数加载。</p>
<p>比较一下默认输出和正常输出。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 第一组</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="function"><span class="keyword">function</span> <span class="title">crc32</span> (<span class="params"></span>)</span>&#123; ... &#125;; <span class="comment">// 输出</span></span><br><span class="line"><span class="keyword">import</span> crc32 <span class="keyword">from</span> <span class="string">'crc32'</span>; <span class="comment">// 输入</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 第二组</span></span><br><span class="line"><span class="keyword">export</span> <span class="function"><span class="keyword">function</span> <span class="title">crc32</span> (<span class="params"></span>)</span>&#123; ... &#125;; <span class="comment">// 输出</span></span><br><span class="line"><span class="keyword">import</span> &#123; crc32 &#125; <span class="keyword">from</span> <span class="string">'crc32'</span>; <span class="comment">// 输入</span></span><br></pre></td></tr></table></figure>

<p>==<code>export default</code> 命令用于指定模块的默认输出。一个模块只能有一个默认输出，因此 <code>export default</code> 命令只能使用一次。所以，<code>import</code> 命令后面才不用加大括号，因为只可能唯一对应 <code>export default</code> 命令。==</p>
<p>本质上，<code>export default</code> 就是输出一个叫做 <code>default</code>  的变量或方法，然后系统允许你为它取任意名字。正因为 <code>export default</code> 命令其实只是输出一个叫做 <code>default</code> 的变量，所以它后面不能跟变量声明语句。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 正确</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">var</span> a = <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 正确</span></span><br><span class="line"><span class="keyword">var</span> a = <span class="number">1</span>;</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> a;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 错误</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">var</span> a = <span class="number">1</span>;</span><br></pre></td></tr></table></figure>

<p>同样地，因为 <code>export default</code> 命令的本质是将后面的值，赋给 <code>default</code> 变量，所以可以直接将一个值写在 <code>export default</code> 之后。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 正确</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="number">42</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 报错</span></span><br><span class="line"><span class="keyword">export</span> <span class="number">42</span>;</span><br></pre></td></tr></table></figure>

<p>有了 <code>export default</code> 命令，输入模块就非常直观了，以输入 <code>lodash</code> 模块为例。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> _ <span class="keyword">from</span> <span class="string">'lodash'</span>;</span><br></pre></td></tr></table></figure>

<p>如果想在一条 <code>import</code> 语句中，同时输入默认方法和其他接口，可以写成下面这样。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> _ , &#123; each, forEach &#125; <span class="keyword">from</span> <span class="string">'lodash'</span>;</span><br><span class="line"><span class="keyword">import</span> React , &#123; Component &#125; <span class="keyword">from</span> <span class="string">'react'</span>;</span><br></pre></td></tr></table></figure>

<h2 id="export-与-import-的复合写法"><a href="#export-与-import-的复合写法" class="headerlink" title="export 与 import 的复合写法"></a>export 与 import 的复合写法</h2><p>如果一个模块之中，先输入后输出同一个模块，<code>import</code> 语句可以与 <code>export</code>  语句写在一起。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">export</span> &#123; foo, bar &#125; <span class="keyword">from</span> <span class="string">'my_module'</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 可以简单理解为</span></span><br><span class="line"><span class="keyword">import</span> &#123; foo, bar &#125; <span class="keyword">from</span> <span class="string">'my_module'</span>;</span><br><span class="line"><span class="keyword">export</span> &#123; foo, bar &#125;;</span><br></pre></td></tr></table></figure>

<p><code>export</code> 和 <code>import</code> 语句可以结合在一起，写成一行。但需要==注意的是，写成一行以后，foo 和 bar 实际上并没有被导入到当前模块，只是相当于对外转发了这两个接口，导致当前模块不能直接使用 foo 和 bar。==</p>
<h2 id="跨模块常量"><a href="#跨模块常量" class="headerlink" title="跨模块常量"></a>跨模块常量</h2><p><code>const</code> 声明的常量只在当前代码块有效。如果想设置跨模块的常量（即跨多个文件），或者说一个值要被多个模块共享，可以采用如下写法。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// constants.js 模块</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> A = <span class="number">1</span>;</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> B = <span class="number">3</span>;</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> C = <span class="number">4</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// test1.js 模块</span></span><br><span class="line"><span class="keyword">import</span> * <span class="keyword">as</span> constants <span class="keyword">from</span> <span class="string">'./constants'</span>;</span><br><span class="line"><span class="built_in">console</span>.log(constants.A); <span class="comment">// 1</span></span><br><span class="line"><span class="built_in">console</span>.log(constants.B); <span class="comment">// 3</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// test2.js 模块</span></span><br><span class="line"><span class="keyword">import</span> &#123; A, B &#125; <span class="keyword">from</span> <span class="string">'./constants'</span>;</span><br><span class="line"><span class="built_in">console</span>.log(constants.A); <span class="comment">// 1</span></span><br><span class="line"><span class="built_in">console</span>.log(constants.B); <span class="comment">// 3</span></span><br></pre></td></tr></table></figure>

<p>如果要使用的常量非常多，可以建一个专门的 <code>constants</code> 目录，将各种常量写在不同的文件里面，保存在该目录下。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// constants/db.js</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> db = &#123;</span><br><span class="line">  url: <span class="string">'http://my.couchdbserver.local:5984'</span>,</span><br><span class="line">  admin_username: <span class="string">'admin'</span>,</span><br><span class="line">  admin_password: <span class="string">'admin password'</span></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// constants/user.js</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> users = [<span class="string">'root'</span>, <span class="string">'admin'</span>, <span class="string">'staff'</span>, <span class="string">'ceo'</span>, <span class="string">'chief'</span>, <span class="string">'moderator'</span>];</span><br></pre></td></tr></table></figure>

<p>然后，将这些文件输出的常量，合并在 <em>index.js</em> 里面。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// constants/index.js</span></span><br><span class="line"><span class="keyword">export</span> &#123; db &#125; <span class="keyword">from</span> <span class="string">'./db'</span>;</span><br><span class="line"><span class="keyword">export</span> &#123; users &#125; <span class="keyword">from</span> <span class="string">'./users'</span>;</span><br></pre></td></tr></table></figure>

<p>使用的时候，直接加载 <em>index.js</em> 就可以了。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// script.js</span></span><br><span class="line"><span class="keyword">import</span> &#123; db, users &#125; <span class="keyword">from</span> <span class="string">'./constants/index'</span>;</span><br></pre></td></tr></table></figure>



      </div>
      
      
      
    </div>
    

    
    
    


          </div>
          

<script>
  window.addEventListener('tabs:register', () => {
    let { activeClass } = CONFIG.comments;
    if (CONFIG.comments.storage) {
      activeClass = localStorage.getItem('comments_active') || activeClass;
    }
    if (activeClass) {
      let activeTab = document.querySelector(`a[href="#comment-${activeClass}"]`);
      if (activeTab) {
        activeTab.click();
      }
    }
  });
  if (CONFIG.comments.storage) {
    window.addEventListener('tabs:click', event => {
      if (!event.target.matches('.tabs-comment .tab-content .tab-pane')) return;
      let commentClass = event.target.classList[1];
      localStorage.setItem('comments_active', commentClass);
    });
  }
</script>

        </div>
          
  
  <div class="toggle sidebar-toggle">
    <span class="toggle-line toggle-line-first"></span>
    <span class="toggle-line toggle-line-middle"></span>
    <span class="toggle-line toggle-line-last"></span>
  </div>

  <aside class="sidebar">
    <div class="sidebar-inner">

      <ul class="sidebar-nav motion-element">
        <li class="sidebar-nav-toc">
          文章目录
        </li>
        <li class="sidebar-nav-overview">
          站点概览
        </li>
      </ul>

      <!--noindex-->
      <div class="post-toc-wrap sidebar-panel">
          <div class="post-toc motion-element"><ol class="nav"><li class="nav-item nav-level-1"><a class="nav-link" href="#ES6-简介"><span class="nav-number">1.</span> <span class="nav-text">ES6 简介</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#let-和-const-命令"><span class="nav-number">2.</span> <span class="nav-text">let 和 const 命令</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#let-命令"><span class="nav-number">2.1.</span> <span class="nav-text">let 命令</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#基本用法"><span class="nav-number">2.1.1.</span> <span class="nav-text">基本用法</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#不存在变量提升"><span class="nav-number">2.1.2.</span> <span class="nav-text">不存在变量提升</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#暂时性死区"><span class="nav-number">2.1.3.</span> <span class="nav-text">暂时性死区</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#不允许重复声明"><span class="nav-number">2.1.4.</span> <span class="nav-text">不允许重复声明</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#块级作用域"><span class="nav-number">2.2.</span> <span class="nav-text">块级作用域</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#const-命令"><span class="nav-number">2.3.</span> <span class="nav-text">const 命令</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#基本用法-1"><span class="nav-number">2.3.1.</span> <span class="nav-text">基本用法</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#本质"><span class="nav-number">2.3.2.</span> <span class="nav-text">本质</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#ES6-声明变量的-6-种方法"><span class="nav-number">2.4.</span> <span class="nav-text">ES6 声明变量的 6 种方法</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#顶层对象属性"><span class="nav-number">2.5.</span> <span class="nav-text">顶层对象属性</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#变量的解构赋值"><span class="nav-number">3.</span> <span class="nav-text">变量的解构赋值</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#数组解构赋值"><span class="nav-number">3.1.</span> <span class="nav-text">数组解构赋值</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#对象的解构赋值"><span class="nav-number">3.2.</span> <span class="nav-text">对象的解构赋值</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#默认值"><span class="nav-number">3.3.</span> <span class="nav-text">默认值</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#字符串的解构赋值"><span class="nav-number">3.4.</span> <span class="nav-text">字符串的解构赋值</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#用途"><span class="nav-number">3.5.</span> <span class="nav-text">用途</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#交换变量的值"><span class="nav-number">3.5.1.</span> <span class="nav-text">交换变量的值</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#从函数返回多个值"><span class="nav-number">3.5.2.</span> <span class="nav-text">从函数返回多个值</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#函数参数的定义"><span class="nav-number">3.5.3.</span> <span class="nav-text">函数参数的定义</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#提取-JSON-数据"><span class="nav-number">3.5.4.</span> <span class="nav-text">提取 JSON 数据</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#函数参数的默认值"><span class="nav-number">3.5.5.</span> <span class="nav-text">函数参数的默认值</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#遍历-Map-结构"><span class="nav-number">3.5.6.</span> <span class="nav-text">遍历 Map 结构</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#输入模块的指定方法"><span class="nav-number">3.5.7.</span> <span class="nav-text">输入模块的指定方法</span></a></li></ol></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#字符串的扩展"><span class="nav-number">4.</span> <span class="nav-text">字符串的扩展</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#字符串的遍历器接口"><span class="nav-number">4.1.</span> <span class="nav-text">字符串的遍历器接口</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#模板字符串"><span class="nav-number">4.2.</span> <span class="nav-text">模板字符串</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#includes-、startsWith-、endsWith"><span class="nav-number">4.3.</span> <span class="nav-text">includes()、startsWith()、endsWith()</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#repeat"><span class="nav-number">4.4.</span> <span class="nav-text">repeat()</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#正则的扩展"><span class="nav-number">5.</span> <span class="nav-text">正则的扩展</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#数值的扩展"><span class="nav-number">6.</span> <span class="nav-text">数值的扩展</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#Number-isFinite-、Number-isNaN"><span class="nav-number">6.1.</span> <span class="nav-text">Number.isFinite()、Number.isNaN()</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Number-parseInt-、Number-parseFloat"><span class="nav-number">6.2.</span> <span class="nav-text">Number.parseInt()、Number.parseFloat()</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Number-isInteger"><span class="nav-number">6.3.</span> <span class="nav-text">Number.isInteger()</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#函数的扩展"><span class="nav-number">7.</span> <span class="nav-text">函数的扩展</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#函数参数默认值"><span class="nav-number">7.1.</span> <span class="nav-text">函数参数默认值</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#rest-参数"><span class="nav-number">7.2.</span> <span class="nav-text">rest 参数</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#箭头函数"><span class="nav-number">7.3.</span> <span class="nav-text">箭头函数</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#箭头函数注意点："><span class="nav-number">7.3.1.</span> <span class="nav-text">箭头函数注意点：</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#this-指向"><span class="nav-number">7.3.2.</span> <span class="nav-text">this 指向</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#不适合场合"><span class="nav-number">7.3.3.</span> <span class="nav-text">不适合场合</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#场合一：定义对象的方法，且该方法内部包括-this"><span class="nav-number">7.3.3.1.</span> <span class="nav-text">场合一：定义对象的方法，且该方法内部包括 this</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#场合二：需要动态-this-的时候，也不应该使用箭头函数"><span class="nav-number">7.3.3.2.</span> <span class="nav-text">场合二：需要动态 this 的时候，也不应该使用箭头函数</span></a></li></ol></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#尾调用优化"><span class="nav-number">7.4.</span> <span class="nav-text">尾调用优化</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#数组的扩展"><span class="nav-number">8.</span> <span class="nav-text">数组的扩展</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#扩展运算符"><span class="nav-number">8.1.</span> <span class="nav-text">扩展运算符</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#扩展运算符的应用"><span class="nav-number">8.2.</span> <span class="nav-text">扩展运算符的应用</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#复制数组"><span class="nav-number">8.2.1.</span> <span class="nav-text">复制数组</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#合并数组"><span class="nav-number">8.2.2.</span> <span class="nav-text">合并数组</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#与解构赋值相结合"><span class="nav-number">8.2.3.</span> <span class="nav-text">与解构赋值相结合</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#字符串"><span class="nav-number">8.2.4.</span> <span class="nav-text">字符串</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#实现了-Iterator-接口的对象"><span class="nav-number">8.2.5.</span> <span class="nav-text">实现了 Iterator 接口的对象</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Array-from"><span class="nav-number">8.3.</span> <span class="nav-text">Array.from()</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Array-of（）"><span class="nav-number">8.4.</span> <span class="nav-text">Array.of（）</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#copyWithin"><span class="nav-number">8.5.</span> <span class="nav-text">copyWithin()</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#find-、findIndex"><span class="nav-number">8.6.</span> <span class="nav-text">find()、findIndex()</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#includes"><span class="nav-number">8.7.</span> <span class="nav-text">includes()</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#对象的扩展"><span class="nav-number">9.</span> <span class="nav-text">对象的扩展</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#属性的简洁表示"><span class="nav-number">9.1.</span> <span class="nav-text">属性的简洁表示</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#属性的可枚举性"><span class="nav-number">9.2.</span> <span class="nav-text">属性的可枚举性</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#属性遍历"><span class="nav-number">9.3.</span> <span class="nav-text">属性遍历</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#super-关键字"><span class="nav-number">9.4.</span> <span class="nav-text">super 关键字</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#链判断运算符"><span class="nav-number">9.5.</span> <span class="nav-text">链判断运算符</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Null-判断运算符"><span class="nav-number">9.6.</span> <span class="nav-text">Null 判断运算符</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#对象的新增方法"><span class="nav-number">10.</span> <span class="nav-text">对象的新增方法</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#Object-is"><span class="nav-number">10.1.</span> <span class="nav-text">Object.is()</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Object-assign"><span class="nav-number">10.2.</span> <span class="nav-text">Object.assign()</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#使用注意点"><span class="nav-number">10.2.1.</span> <span class="nav-text">使用注意点</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#（1）潜拷贝"><span class="nav-number">10.2.1.1.</span> <span class="nav-text">（1）潜拷贝</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#（2）同名属性的替换"><span class="nav-number">10.2.1.2.</span> <span class="nav-text">（2）同名属性的替换</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#（3）数组的处理"><span class="nav-number">10.2.1.3.</span> <span class="nav-text">（3）数组的处理</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#（4）取值函数的处理"><span class="nav-number">10.2.1.4.</span> <span class="nav-text">（4）取值函数的处理</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#（5）undefind、null"><span class="nav-number">10.2.1.5.</span> <span class="nav-text">（5）undefind、null</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#常见用途"><span class="nav-number">10.2.2.</span> <span class="nav-text">常见用途</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#（1）为对象添加属性"><span class="nav-number">10.2.2.1.</span> <span class="nav-text">（1）为对象添加属性</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#（2）为对象添加方法"><span class="nav-number">10.2.2.2.</span> <span class="nav-text">（2）为对象添加方法</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#（3）克隆对象"><span class="nav-number">10.2.2.3.</span> <span class="nav-text">（3）克隆对象</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#（4）合并多个对象"><span class="nav-number">10.2.2.4.</span> <span class="nav-text">（4）合并多个对象</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#（5）为属性指定默认值"><span class="nav-number">10.2.2.5.</span> <span class="nav-text">（5）为属性指定默认值</span></a></li></ol></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Object-getOwnPropertyDescriptors"><span class="nav-number">10.3.</span> <span class="nav-text">Object.getOwnPropertyDescriptors()</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#proto-属性"><span class="nav-number">10.4.</span> <span class="nav-text">__proto__ 属性</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Object-setPrototypeOf-、Object-getPrototypeOf"><span class="nav-number">10.5.</span> <span class="nav-text">Object.setPrototypeOf()、Object.getPrototypeOf()</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Object-keys-、Object-values-、Object-entries"><span class="nav-number">10.6.</span> <span class="nav-text">Object.keys()、Object.values()、Object.entries()</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#Symbol"><span class="nav-number">11.</span> <span class="nav-text">Symbol</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#概述"><span class="nav-number">11.1.</span> <span class="nav-text">概述</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#作为属性名的-Symbol"><span class="nav-number">11.2.</span> <span class="nav-text">作为属性名的 Symbol</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#属性名的遍历"><span class="nav-number">11.3.</span> <span class="nav-text">属性名的遍历</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Symbol-for-、Symbol-keyFor"><span class="nav-number">11.4.</span> <span class="nav-text">Symbol.for()、Symbol.keyFor()</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#Set、WeakSet"><span class="nav-number">12.</span> <span class="nav-text">Set、WeakSet</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#set-简介"><span class="nav-number">12.1.</span> <span class="nav-text">set 简介</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Set-实例的属性和方法"><span class="nav-number">12.2.</span> <span class="nav-text">Set 实例的属性和方法</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Set-结构遍历"><span class="nav-number">12.3.</span> <span class="nav-text">Set 结构遍历</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#keys-，values-，entries"><span class="nav-number">12.3.1.</span> <span class="nav-text">keys()，values()，entries()</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#forEach"><span class="nav-number">12.3.2.</span> <span class="nav-text">forEach()</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#遍历的应用"><span class="nav-number">12.3.3.</span> <span class="nav-text">遍历的应用</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#WeakSet"><span class="nav-number">12.4.</span> <span class="nav-text">WeakSet</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#简介"><span class="nav-number">12.4.1.</span> <span class="nav-text">简介</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#语法"><span class="nav-number">12.4.2.</span> <span class="nav-text">语法</span></a></li></ol></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#Map、WeakMap"><span class="nav-number">13.</span> <span class="nav-text">Map、WeakMap</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#Map-简介"><span class="nav-number">13.1.</span> <span class="nav-text">Map 简介</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Map-实例属性和操作方法"><span class="nav-number">13.2.</span> <span class="nav-text">Map 实例属性和操作方法</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Map-结构遍历"><span class="nav-number">13.3.</span> <span class="nav-text">Map 结构遍历</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#与其他数据结构的相互转换"><span class="nav-number">13.4.</span> <span class="nav-text">与其他数据结构的相互转换</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#Map-转为数组"><span class="nav-number">13.4.1.</span> <span class="nav-text">Map 转为数组</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#数组转为-Map"><span class="nav-number">13.4.2.</span> <span class="nav-text">数组转为 Map</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Map-转为对象"><span class="nav-number">13.4.3.</span> <span class="nav-text">Map 转为对象</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#对象转为-Map"><span class="nav-number">13.4.4.</span> <span class="nav-text">对象转为 Map</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#WeakMap"><span class="nav-number">13.5.</span> <span class="nav-text">WeakMap</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#简介-1"><span class="nav-number">13.5.1.</span> <span class="nav-text">简介</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#语法-1"><span class="nav-number">13.5.2.</span> <span class="nav-text">语法</span></a></li></ol></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#Proxy"><span class="nav-number">14.</span> <span class="nav-text">Proxy</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#概述-1"><span class="nav-number">14.1.</span> <span class="nav-text">概述</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Proxy-实例的方法列表"><span class="nav-number">14.2.</span> <span class="nav-text">Proxy 实例的方法列表</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Proxy-实例方法详解（仅列举部分常用方法）"><span class="nav-number">14.3.</span> <span class="nav-text">Proxy 实例方法详解（仅列举部分常用方法）</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#get"><span class="nav-number">14.3.1.</span> <span class="nav-text">get()</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#set"><span class="nav-number">14.3.2.</span> <span class="nav-text">set()</span></a></li></ol></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#Reflect"><span class="nav-number">15.</span> <span class="nav-text">Reflect</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#概述-2"><span class="nav-number">15.1.</span> <span class="nav-text">概述</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#静态方法"><span class="nav-number">15.2.</span> <span class="nav-text">静态方法</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#Reflect-get-target，name，receiver"><span class="nav-number">15.2.1.</span> <span class="nav-text">Reflect.get(target，name，receiver)</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Reflect-set-target，name，value，receiver"><span class="nav-number">15.2.2.</span> <span class="nav-text">Reflect.set(target，name，value，receiver)</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Reflect-has-obj，name"><span class="nav-number">15.2.3.</span> <span class="nav-text">Reflect.has(obj，name)</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Reflect-deleteProperty-obj，name"><span class="nav-number">15.2.4.</span> <span class="nav-text">Reflect.deleteProperty(obj，name)</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Reflect-construct-target，args"><span class="nav-number">15.2.5.</span> <span class="nav-text">Reflect.construct(target，args)</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Reflect-getPrototypeOf-obj"><span class="nav-number">15.2.6.</span> <span class="nav-text">Reflect.getPrototypeOf(obj)</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Reflect-setPrototypeOf-obj，newProto"><span class="nav-number">15.2.7.</span> <span class="nav-text">Reflect.setPrototypeOf(obj，newProto)</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Reflect-apply-func，thisArg，args"><span class="nav-number">15.2.8.</span> <span class="nav-text">Reflect.apply(func，thisArg，args)</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Reflect-defineProperty-target，propertyKey，attributes"><span class="nav-number">15.2.9.</span> <span class="nav-text">Reflect.defineProperty(target，propertyKey，attributes)</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Reflect-getOwnPropertyDescriptor-target，propertyKey"><span class="nav-number">15.2.10.</span> <span class="nav-text">Reflect.getOwnPropertyDescriptor(target，propertyKey)</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Reflect-isExtensible-target"><span class="nav-number">15.2.11.</span> <span class="nav-text">Reflect.isExtensible(target)</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Reflect-preventExtensions-target"><span class="nav-number">15.2.12.</span> <span class="nav-text">Reflect.preventExtensions(target)</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Reflect-ownKeys-target"><span class="nav-number">15.2.13.</span> <span class="nav-text">Reflect.ownKeys(target)</span></a></li></ol></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#Promise-对象"><span class="nav-number">16.</span> <span class="nav-text">Promise 对象</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#Promise-含义"><span class="nav-number">16.1.</span> <span class="nav-text">Promise 含义</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#基本用法-2"><span class="nav-number">16.2.</span> <span class="nav-text">基本用法</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Promise-prototype-then"><span class="nav-number">16.3.</span> <span class="nav-text">Promise.prototype.then()</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Promise-prototype-catch"><span class="nav-number">16.4.</span> <span class="nav-text">Promise.prototype.catch()</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Promise-prototype-finally"><span class="nav-number">16.5.</span> <span class="nav-text">Promise.prototype.finally()</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Promise-all"><span class="nav-number">16.6.</span> <span class="nav-text">Promise.all()</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Promise-race"><span class="nav-number">16.7.</span> <span class="nav-text">Promise.race()</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Promise-resolve"><span class="nav-number">16.8.</span> <span class="nav-text">Promise.resolve()</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#Promise-reject"><span class="nav-number">16.9.</span> <span class="nav-text">Promise.reject()</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#Iterator-和-for-…-of-循环"><span class="nav-number">17.</span> <span class="nav-text">Iterator 和 for … of 循环</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#Iterator-概述"><span class="nav-number">17.1.</span> <span class="nav-text">Iterator 概述</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#默认-Iterator-接口"><span class="nav-number">17.2.</span> <span class="nav-text">默认 Iterator 接口</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#for-…-of-循环"><span class="nav-number">17.3.</span> <span class="nav-text">for … of 循环</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#数组"><span class="nav-number">17.3.1.</span> <span class="nav-text">数组</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Set-和-Map-结构"><span class="nav-number">17.3.2.</span> <span class="nav-text">Set 和 Map 结构</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#计算生成的数据结构"><span class="nav-number">17.3.3.</span> <span class="nav-text">计算生成的数据结构</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#类似数组的对象"><span class="nav-number">17.3.4.</span> <span class="nav-text">类似数组的对象</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#对象"><span class="nav-number">17.3.5.</span> <span class="nav-text">对象</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#与其他遍历语法的比较"><span class="nav-number">17.3.6.</span> <span class="nav-text">与其他遍历语法的比较</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#以数组为例"><span class="nav-number">17.3.6.1.</span> <span class="nav-text">以数组为例</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#for-in-循环有几个缺点"><span class="nav-number">17.3.6.2.</span> <span class="nav-text">for ... in 循环有几个缺点</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#for-of-的优势"><span class="nav-number">17.3.6.3.</span> <span class="nav-text">for ... of 的优势</span></a></li></ol></li></ol></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#Class-基本用法"><span class="nav-number">18.</span> <span class="nav-text">Class 基本用法</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#简介-2"><span class="nav-number">18.1.</span> <span class="nav-text">简介</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#类的由来"><span class="nav-number">18.1.1.</span> <span class="nav-text">类的由来</span></a></li></ol></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#Module-用法"><span class="nav-number">19.</span> <span class="nav-text">Module 用法</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#概述-3"><span class="nav-number">19.1.</span> <span class="nav-text">概述</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#严格模式"><span class="nav-number">19.2.</span> <span class="nav-text">严格模式</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#export-命令"><span class="nav-number">19.3.</span> <span class="nav-text">export 命令</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#import-命令"><span class="nav-number">19.4.</span> <span class="nav-text">import 命令</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#模块的整体加载"><span class="nav-number">19.5.</span> <span class="nav-text">模块的整体加载</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#export-default-命令"><span class="nav-number">19.6.</span> <span class="nav-text">export default 命令</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#export-与-import-的复合写法"><span class="nav-number">19.7.</span> <span class="nav-text">export 与 import 的复合写法</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#跨模块常量"><span class="nav-number">19.8.</span> <span class="nav-text">跨模块常量</span></a></li></ol></li></ol></div>
      </div>
      <!--/noindex-->

      <div class="site-overview-wrap sidebar-panel">
        <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
    <img class="site-author-image" itemprop="image" alt="Albert Guo"
      src="https://inews.gtimg.com/newsapp_ls/0/12122532695/0">
  <p class="site-author-name" itemprop="name">Albert Guo</p>
  <div class="site-description" itemprop="description">郭鹏松的个人博客</div>
</div>
<div class="site-state-wrap motion-element">
  <nav class="site-state">
      <div class="site-state-item site-state-posts">
          <a href="/archives">
          <span class="site-state-item-count">19</span>
          <span class="site-state-item-name">日志</span>
        </a>
      </div>
      <div class="site-state-item site-state-categories">
            <a href="/categories/">
          
        <span class="site-state-item-count">6</span>
        <span class="site-state-item-name">分类</span></a>
      </div>
  </nav>
</div>
  <div class="links-of-author motion-element">
      <span class="links-of-author-item">
        <a href="mailto:albert-guo@outlook.com" title="E-Mail → mailto:albert-guo@outlook.com" rel="noopener" target="_blank"><i class="fa fa-envelope fa-fw"></i>E-Mail</a>
      </span>
  </div>



      </div>

    </div>
  </aside>
  <div id="sidebar-dimmer"></div>


      </div>
    </main>

    <footer class="footer">
      <div class="footer-inner">
        

        

<div class="copyright">
  
  &copy; 
  <span itemprop="copyrightYear">2020</span>
  <span class="with-love">
    <i class="fa fa-heart"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">Albert Guo</span>
</div>
  <div class="powered-by">由 <a href="https://hexo.io/" class="theme-link" rel="noopener" target="_blank">Hexo</a> & <a href="https://theme-next.org/" class="theme-link" rel="noopener" target="_blank">NexT.Gemini</a> 强力驱动
  </div>

        








      </div>
    </footer>
  </div>

  
  <script src="/lib/anime.min.js"></script>
  <script src="/lib/velocity/velocity.min.js"></script>
  <script src="/lib/velocity/velocity.ui.min.js"></script>

<script src="/js/utils.js"></script>

<script src="/js/motion.js"></script>


<script src="/js/schemes/pisces.js"></script>


<script src="/js/next-boot.js"></script>




  















  

  

</body>
</html>
